When “if else”/“instance of” are inevitable, how do we improve the design apart from using visitor pattern?

偶尔善良 提交于 2020-01-23 07:07:52

问题


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.


回答1:


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.




回答2:


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 }



回答3:


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!