I am having a bit of a conundrum.
I have 2 objects A
and B
.
They can come as null
or not null
, and I have to do some different business logic in all of those situations.
At the moment I have:
if(A != null && B != null) { /* operation1 */ }
if(A == null && B == null) { /* operation2 */ }
if(A != null && B == null) { /* operation3 */ }
if(A == null && B != null) { /* operation4 */ }
Can you recommend me a way of writing those if
s in a more elegant way?
In fact, if you want to keep your if conditions visible in the calling method, your code is a better option than the solution proposed to imbricate the if conditions. All those if-else and inner if-else statements make it less obvious what the conditions are that trigger each operation. So you can keep your approach. See this blog post from Jeff Atwood
One small improvement you can do is extract methods from the if conditions and name the new methods in a way that documents your decisions:
if( isYouHaveACarAndEnoughGas(A, b)) { //operation to drive}
if( isYouHaveACarAndNoGas(A, b)) { //operation to walk}
...
private boolean isYouHaveACarAndEnoughGas(...){ return A != null && B != null; }
If you want/need something more fancy, you can use a command pattern or strategy. As a rule of thumb, repeated if-else statements or switches are signs you can use polymorphism to get the same result. See this excellent post with examples.
interface Operation{
void execute();
}
class OperationBuilder {
public static Operation build(TypeA A, TypeB B){
if(A != null && B != null) { return new OperationOne(); }
if(A == null && B == null) { return new OperationTwo(); }
if(A != null && B == null) { /* operation3 */ }
if(A == null && B != null) { /* operation4 */ }
}
class OperationOne implements Operation{
private OperationOne(){...}
@Override
void execute(){...}
}
class OperationTwo implements Operation{
private OperationTwo(){}
@Override
void execute(){...}
}
}
To use this you just
Operation op = OperationBuilder.build(A, B) ;
op.execute();
And here is why this can be better than what you started of. Imagine you have
if(codition1){
//complicated stuff here that makes you scroll and scroll
}
if(codition2){
//complicated stuff here that makes you scroll and scroll
}
etc
You are likely to not see all the conditions on the same screen so you lose the overview of what's going on. On the other hand, the builder keeps all the conditions tight and clean, while the operational logic is again separated from noise.