Currently I am cleaning up hard to maintain and test if else clutter which is based on conditions which have to be checked in isolations:
What is the basic semantic of the conditions?
Big Entity Objects have to be checked based on two entity keys namely Trans and Right for state change as in the example Below:
if (oldTrans.getfOrder().equals(newTrans.getfOrder()) {
compound.setIsStateChanged(true);
return;
}
if (oldRight.getgRight().equals(newRight.getgRight()) {
compound.setIsStateChanged(true);
}
....and the list goes on till 20 more such conditions
Currently the if else are all cluttered up at one place:
if (oldTrans.getfOrder().equals(newTrans.getfOrder()) {
compound.setIsStateChanged(true);
LOGGER.info("major change detected");
return compound;
} if (oldTrans.getgOrder().equals(newTrans.getgOrder()) {
compound.setIsStateChanged(true);
LOGGER.info("major change detected");
return compound;
}
I see 2 main issues here
Every if has a return statement and with so many ifs it hard to know when and a what point method exits.
To many if branchings are error prone and The number of conditions is likely to go up.
To avoid so many ifs that are basically based on the same semantic underneath from clean code perspective I tried to solve it the polymorphic way
Extracting the Conditions in Enums as Constants and impleneting a checker Interface that takes new and old objects as params
public interface ICheckStateChange<T>(T old, T new) {
boolean check(T old, T new);
}
//implementations
public TransChecker implements ICheckStateChange<Trans> {
List<BiPredicate<Trans, Trans>> allTransConditions = transConditions.getValues();
public boolean check(Trans oldTrans, Trans newTrans) {
//all conditions null check here
//loop through conditions
for (BiPredicate<Trans, Trans> transCondition: allTransConditions) {
if (transCondition).test()) {
return true;
LOGGER.info("major state change detected, taking apt action")
}
}
public RightChecker implements ICheckStateChange<Right> {
List<BiPredicate<Right, Right>> allTransConditions = RightConditions.getValues();
public boolean check(Right oldRight, Right newRIght) {
//all conditions null check here
//loop through conditions
for (BiPredicate<Right, Right> rightCondition: allRightConditions) {
if (rightCondition).test()) {
return true;
LOGGER.info("major state change detected, taking apt action")
}
}
The Conditons are now centrally located as BiPredicate constants using lambdas
public enum rightConditions {
FORDER_CHANGE_NULL_TO_NOT_NULL((Order old, Order new)
-> old == null && new != null),
//to be replaced by the right condition
GORDER_CHANGE_FROM_OPEN_TO_DONE((Order old, Order new)
-> old == null && new != null)
//to be replaced by the right condition
LORDER_CHANGE_FROM_OPEN_TO_REVERTED((Order old, Order new)
-> old == null && new != null)
}
My question here is about the approach of refactoring the If elses with the help of lambda BiPredicates in hindsight of clean code? Readability, extensibility and maintainability ;)