Reputation: 8902
Sometimes I'm faced with a routine that will test some conditions and either continue or exit, something like the following code:
public void execute() {
if (context.condition1()) {
LOGGER.info("Condition 1 not satisfied.");
return;
}
doSomeStuff();
if (context.condition2()) {
LOGGER.info("Condition 2 not satisfied.");
return;
}
if (context.condition3()) {
LOGGER.info("Condition 3 not satisfied.");
return;
}
doRestOfStuff();
}
Let's assume there is no way to test these conditions before entrying this method. Maybe this is a servlet or a controller with this single entry point. From my experience (which is far from vast), this seems to be a common situation.
Now, this code doesn't smell good, does it? What would be the best strategy to handle this?
Upvotes: 2
Views: 1519
Reputation: 1838
Move your logging side effects into the condition methods and stream the operations
public void execute() {
Stream.of(context)
.filter(c -> c.condition1())
.peek(this::doSomeStuff)
.filter(c -> c.condition2())
.filter(c -> c.condition3())
.forEach(this::doRestOfStuff);
}
Upvotes: 0
Reputation: 31648
How many different exit points are we talking about?
If it's only 3 like in your example, then your original code is just fine. It's easy to read. I would consider the "only have one exit point" as a general guide line and not a hard set rule.
Changing it to a nested group of if
/ else if
blocks I find harder to read especially after indenting. Also you run the risk of forgetting to properly place new conditions to the chain if you have to add more condition checks in the future.
If however, you have a lot of conditions and you might need to add many more, possibly at runtime, then you might be able to leverage the Chain of Responsibility
pattern.
The end result would look something like this:
public void execute() {
List<Condition> conditionChains = ....
for(Condition condition : conditionChain){
if(condition.notSatisfied(context)){
//here use condition#toString() to explain what wasn't satisfied.
//Could use another method like getDescrption() instead...
LOGGER.info(condition + " not satisfied.");
return;
}
condition.execute(context);
}
}
Where you would have an interface Condition
that had 2 methods to check satisfability and then another to execute any relevant code.
Example implementations:
class Condition1 implements Condition{
public boolean isSatisfied(Context context){
...
}
public void execute(Context context){
doSomeStuff();
}
}
class Condition2 implements Condition{
public boolean isSatisfied(Context context){
...
}
public void execute(Context context){
//do nothing
}
}
class Condition3 implements Condition{
public boolean isSatisfied(Context context){
return !context.notCondition3();
}
public void execute(Context context){
doRestOfStuff();
}
}
Upvotes: 3
Reputation: 2094
Try to read about switch Statement, it is the solution in many languages and specifically in java:
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html
Upvotes: 1
Reputation: 8902
This is what I've came up with until now:
public void execute() {
checkCondition1AndExecute();
}
private void checkCondition1AndExecute() {
if (context.notCondition1()) {
doSomeStuff();
checkCondition2AndExecute();
} else {
LOGGER.info("Condition 1 not satisfied.");
}
}
private void checkCondition2AndExecute() {
if (context.notCondition2()) {
checkCondition3AndExecute();
} else {
LOGGER.info("Condition 2 not satisfied.");
}
}
private void checkCondition3AndExecute() {
if (context.notCondition3()) {
doRestOfStuff();
} else {
LOGGER.info("Condition 3 not satisfied.");
}
}
It doesn't look great but it does smell better for me. Instead of one big method with multiple exits, I have small methods, each one responsible for one specific condition (and exit point). Also, it gets rid of the dumb return;
lines.
However, I'm still wondering if there might be a pattern or just a better strategy to handle this.
Upvotes: 0