I'm having a problem implementing a strategy pattern for a specific problem i'm encountering.
I basically have a method makes a comparison between values. Apart from the two values, this method relies on two additional parameters to determine which comparison i should do: an operator (equals, not equals,...) and a type (string, double) It is basically a switch which relies on the outcome of another switch.
Let me clarify with an example:
public enum Type {
STRING,
BOOLEAN,
DOUBLE;
}
public enum Operator {
EQUALS(new Type[] { Type.STRING, Type.BOOLEAN}),
NOT_EQUALS(new Type[] { Type.STRING, Type.BOOLEAN});
private Type[] allowedTypes;
Operator(Type[] allowedTypes) {
this.allowedTypes = allowedTypes;
}
public List<Type> getAllowedTypes() {
return Arrays.asList(allowedTypes);
}
}
public class Evaluator {
public Boolean evaluate(String value1, String value2, Operator operator, Type type) {
switch(operator) {
case EQUALS:
return doEqualComparison(value1, value2, type);
}
return null;
}
private Boolean doEqualComparison(String value1, String value2, Type type) {
//code here to check if the params are correct (not null, etc)
switch(type) {
case STRING:
return value1.equals(value2);
case DOUBLE:
return Double.parseDouble(value1) == Double.parseDouble(value2);
}
return null;
}
}
And the test
public class EvaluatorTest {
@Test
public void testEvaluate() {
Evaluator evaluator = new Evaluator();
// check STRING
Assert.assertTrue(evaluator.evaluate("100", "100", Operator.EQUALS, Type.STRING));
Assert.assertFalse(evaluator.evaluate("100.00", "100", Operator.EQUALS, Type.STRING));
// check DOUBLE
Assert.assertTrue(evaluator.evaluate("100", "100", Operator.EQUALS, Type.DOUBLE));
Assert.assertTrue(evaluator.evaluate("100.00", "100", Operator.EQUALS, Type.DOUBLE));
}
}
This works fine but having two (essentially nested) switch statements doesn't seem like a very clean and future proof solution, in case we add alot of Types and Operators things get nasty, pretty quick, so my first refactoring was to change the first switch (Operator) to a Strategy
public class EqualComparisonStrategy implements ComparisonStrategy {
@Override
public Boolean compare(String value1, String value2, Type Type) {
//HERE IS MY PROBLEM
switch (conditionType) {
case STRING:
return value1.equals(conditionValue);
case DOUBLE:
return Double.parseDouble(value1) == Double.parseDouble(value2);
}
return null;
}
@Override
public boolean accept(Operator operator) {
return operator == Operator.EQUAL;
}
}
Although that solution is not "too" bad i guess i would like to externalize the second switch (where my comment is) too, problem is that the implementation of it relies on the the first (operator). I thought about something like StringEqualComparisonStrategy made by either a factory or just do some more stuff in the accept method
//snippet of the accept methode (instead of a factory)
@Override
public boolean accept(Operator operator, Type type) {
return operator == Operator.EQUAL && type == Type.STRING;
}
But this creates M * N strategies and gets out of hand real fast (i have about 8 operators, with 3 types with results in 24 strategies) Seeing as i want this a future proof as possible this is not the best solution IMHO.
I'm sure there must be a pattern that fixes this problem, but i'm not seeing it. (i'm no pattern hero)
Can someone help?