Search code examples
javadesign-patternsswitch-statementstrategy-pattern

Pattern Strategy vs Switch OR How to handle a large number of different operations


I want to get rid of Switch with different operations in the code. Can this be done using the Strategy pattern in this case (or is there another way?):

public interface Strategy {
    BigDecimal minus(BigDecimal a, BigDecimal b);
    BigDecimal sum(BigDecimal a, BigDecimal b);
    BigDecimal pow(BigDecimal a, int n);
}

public class Minus implements Strategy {
    public BigDecimal minus(BigDecimal a, BigDecimal b) {
        return a.subtract(b);
    }
}

public class Sum implements Strategy{
    public BigDecimal sum(BigDecimal a, BigDecimal b) {
        return a.add(b);
    }

    public BigDecimal pow(BigDecimal a, int n) {
        return a.pow(n);
    }
}

public class Calc {
    private Strategy strategy;
    private BigDecimal a;
    private BigDecimal b;
    private int n;

    public Calc(Strategy strategy, BigDecimal a, BigDecimal b, int n) {
        this.strategy = strategy;
        this.a = a;
        this.b = b;
        this.n = n;
    }

    public void calculate(String operation) {
        switch (operation) {
            case "SUM":
                strategy.sum(a, b);
                break;
            case "POW":
                strategy.pow(a, n);
                break;
            case "MINUS":
                strategy.minus(a, b);
        }
    }
}

ps: the code doesn't work, because I don't understand how to implement the Strategy interface without removing the pow method from the Sum class.


Solution

  • You have some kind of misunderstanding about the strategy pattern. Simply said, by imlementing it, you have classes, which each provides a special manifestation of a behaviour. You than select from all these possible manifestations, which one to use for your special case. Your interface Strategy - lets better name it TwoParamOperation would then look somehow like this:

    public interface TwoParamOperation {
        BigDecimal compute(BigDecimal operand1, BigDecimal operand2);
    }
    

    Your actual implementations oft it might be:

    public class Minus implements TwoParamOperation{
        @Override
        public BigDecimal compute(BigDecimal operand1, BigDecimal operand2) {
            return operand1.subtract(operand2);
        }
    }
    
    public class Pow implements TwoParamOperation{
        @Override
        public BigDecimal compute(BigDecimal operand1, BigDecimal operand2) {
            return operand1.pow(operand2.intValue());
        }
    }
    

    Then you have to implement a mechanism to select from these strategies. You could simply have a map whith the operation name as key and the instance of the strategy as value:

    public class Calc {

    private Map<String,TwoParamOperation> operations = new HashMap<>();
    
    public Calc(){
        add("MINUS", new Minus());
        add("POW", new Pow());
    }
    
    public void add(String opName,TwoParamOperation operation){
        operations.put(opName,operation);
    }
        
        public Optional<BigDecimal> calculate(String opName, BigDecimal operand1, BigDecimal operand2){
        TwoParamOperation operation = operations.get(opName);
        if (operation!= null){
            return Optional.of(operation.compute(operand1,operand2));
        }
        return Optional.empty();
    }
    

    }

    This way you can even add new operations. Removal is up to you.. By calling

    Calc calc = new Calc();
    
    BigDecimal x=calc.calculate("MINUS",BigDecimal.valueOf(2.3),BigDecimal.valueOf(42)).orElseThrow();
    

    you get a result for your selected operation on the two parameters, if such an operation is implemented.

    If you need operations with other parameters or result types you might add them as extra maps of other Interfaces.