When we have an object hierarchy that is purely a inheritance of semantic and not of behaviors,then inevitably we need to write "instanceof" or "if/else" everywhere to do run time type checking.
E.g.
If I have a object hierarchy which has
Class Function
Class Average extends Function
Class Sum extends Function
Class Max extends Function
If there is a method called calculate() in these classes, then we do not have problem, we can just take the advantage of polymorphism and this design satisfies the LSP.
However what if we do not want to add this calculate() method to this hierarchy for some reason, these objects are purely plain object stateless objects just represent the semantic.
Then we are forced to write the following code everywhere :
if (function instanceof Average)
//perform average
else if(function instanceof Sum)
//perform sum
else if(function instanceof Max)
//perform max
The code above indicates a bad design, because you write this code everywhere and this design is fragile and is hard to change later on. I guess if the number functions are limited and the calculation of function are in a single place this perhaps is ok depends on the complexity.
What I've known so far is that to solve above approach, the only possible way is to implement a visitor pattern, is there any other way to solve the above design apart from using visitor pattern?
One problem I can see from visitor pattern is that visitor pattern's accept method does not return value, this is not convenient sometime if the accept() method doesn't fully satisfy the requirement.
If you still know the types at compile time you can use a helper class:
class Function {
}
class Average extends Function {
}
class Sum extends Function {
}
class Max extends Function {
}
class FunctionHelper {
public Number calculate(Average a) {
return null;
}
public Number calculate(Sum s) {
return null;
}
public Number calculate(Max a) {
return null;
}
public Number calculate(Function a) {
return null;
}
}
Generally you'd make the helper methods static but you are not restricted to that - there are some rather interesting things you can do with multiple flavours of helper class.
these objects are purely plain object stateless objects just represent the semantic.
Sounds like you want to use enums then instead of regular objects.
Then you can use switch
statements and have the compiler check that you handled all cases.
enum Function { Average, Sum, Max }
i just stumbled over the Chain of Responsibilty pattern which might solve your Problem:
For this we create a handler class for every Function starting with a common base class
public abstract class FunctionHandler {
private FunctionHandler nexthandler = null;
protected abstract boolean isConditionMet(Function function);
protected abstract void calculate(Function function);
public void handleFunction(Function function) {
if(function == null) {
return;
}
if (isConditionMet(function)) {
calculate(function);
} else {
if (nexthandler != null) {
nexthandler.handleFunction(function);
}
}
}
public FunctionHandler setNexthandler(FunctionHandler nexthandler) {
this.nexthandler = nexthandler;
return nexthandler;
}
}
next we create the concrete handlers:
public class Averagehandler extends FunctionHandler {
@Override
protected boolean isConditionMet(Function function) {
return function instanceof Average;
}
@Override
protected void calculate(Function function) {
// do average stuff
}
}
public class SumHandler extends FunctionHandler {
@Override
protected boolean isConditionMet(Function function) {
return function instanceof Sum;
}
@Override
protected void calculate(Function function) {
// do sum stuff
}
}
public class MaxHandler extends FunctionHandler {
@Override
protected boolean isConditionMet(Function function) {
return function instanceof Max;
}
@Override
protected void calculate(Function function) {
// do max stuff
}
}
now look for a nice central place to put your chain together (e.g. a utility class)
public final class FunctionUtil {
public static final FunctionHandler HANDLER;
static {
HANDLER = new Averagehandler();
HANDLER.setNexthandler(new SumHandler()).setNexthandler(new MaxHandler());
}
}
now you can start replacing your if-else-blocks with calls to FunctionUtil.HANDLER.handleFunction(function);
来源:https://stackoverflow.com/questions/16665898/when-if-else-instance-of-are-inevitable-how-do-we-improve-the-design-apart