I am trying to re-factor a legacy code and in this case I have a huge block of switch case which decide which command to be executed
switch(operation)
case addition : return add(int a, String b, String c);
case multiply : return multiply(int a, int b);
case substract : return substract(int a, int b);
Approach 1 : using polymorphism
public interface Operation {
void performOperation(int a, int b);
}
Then fill a map with the available implementations:
Map<Key, Operation> actions = new HashMap<>();
actions.add(addition, new addOperation());
actions.add(multiply, new multiplyOperation());
actions.add(substract, new substractOperation());
Then I can refer the map when I need to perform a operation.
The issues I have with this approach is that I am having to create a large number of classes / annonymous classes
Approach 2 : Using Enum
public enum MyKeyEnum {
ADDITION {
public void performOperation(int a, int b) {
// Perform addition
}
},
MULTIPLY {
public void performOperation(int a, int b) {
// Perform Multiplication
}
};
public abstract void performOperation(int counter, String dataMain, String dataSub);
}
This approach is actually better of the two but I saw another eaxmple in Java 8 and want use something like this
As all these are following a pattern I tried to use Functional Interface and Maps
final static Map<String, Supplier<IAction>> map = new HashMap<>();
static {
map.put("add", Addition::new);
map.put("multiply", Multiply::new);
}
public static void main(String[] args) throws Exception {
Supplier<IAction> action = map.get("add");
System.out.println(action.get().performAction(10,10));
action = map.get("multiply");
System.out.println(action.get().performAction(10,10));
}
But this again has the disadvantages of the first approach so wanted to see if I can use lambdas like I used Enum implementation There is a partial function implementation provided in Java 8 which I wanted to utilize Example :
BiFunction<Integer, Integer, Integer> minus = (x, y) -> x - y;
Function<Integer, Integer> subtractor = partial(minus, 10);
System.out.println(subtractor.apply(4)); // 6
as BiFunction is accepting only 2 parameters I created a Trifuction like
@FunctionalInterface
interface TriFunction<T, U, V, R> {
R apply(T a, U b, V c);
}
public static <T, U, V, R> Function<V, R> partial(TriFunction<T, U, V, R> f, T x, U y) {
return (z) -> f.apply(x, y, z);
}
This will resolve the issue to an extent but I am not able to figure out how I can add this to the map and dynamically pass values
Map<String, TriFunction<String, Integer, Integer, Operation>> map
= new HashMap<>();
You are already there. If you've a method which has the same signature of your interface you can also pass it to your operation repository like:
Map<String, IntBinaryOperator> operations = new HashMap<>();
operations.put("add", Integer::sum);
operations.put("subtract", (a, b) -> a - b);
operations.put("multiply", (a, b) -> a * b);
//...
System.out.println(operations.get("multiply").applyAsInt(10, 20));
Thx for your question, as i stumbled on the exact same problem. I was looking for a way to eliminate if if if or switch blocks in my code for some time.
I opted for the Map + Supplier approach, implemented within a factory, as in the following snippet:
public class OperatorFactory {
private final String supportedOperatorsRegex = "\\+|\\*|-";
private Map<String, Supplier<Operator>> supportedOperators = ImmutableMap.of(
"+", Addition::new, "*", Multiplication::new, "-", Subtraction::new, "ID", Identity::new
);
public Operator parseToOperator(String input) {
return supportedOperators.get(extractOperator(input)).get();
}
private String extractOperator(String input) {
Matcher matcher = Pattern.compile(supportedOperatorsRegex).matcher(input);
if (matcher.find()) return matcher.group();
if (input.matches("\\d+")) return "ID";
throw new UnsupportedOperationException("unsupported operation: " + input);
}
}
I find this sollution closer to the Open / Close Principle (the O in SOLID principles):
Operator interface that i have defined elsewhere, add it in the supportedOperators map and in the SupportedOperatorsRegex without changing any of the business logic of the OperatorFactory class.OperatorFactory, and that is less coupling.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With