使用 java lambdas 重构 switch case

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33603856/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 21:54:20  来源:igfitidea点击:

Refactor a switch case with java lambdas

javalambda

提问by Sudheer

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 case 来决定要执行的命令

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

这种方法实际上是两者中更好的,但我在 Java 8 中看到了另一个 eaxmple 并且想要使用这样的东西

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 :

但这又具有第一种方法的缺点,所以想看看我是否可以像使用 Enum 实现一样使用 lambdas Java 8 中提供了一个部分函数实现,我想利用示例:

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

因为 BiFunction 只接受 2 个参数,所以我创建了一个 Trifuction 像

@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<>();

采纳答案by Flown

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));

回答by avi.elkharrat

Thx for your question, as i stumbled on the exact same problem. I was looking for a way to eliminate if if ifor switchblocks in my code for some time.

谢谢你的问题,因为我偶然发现了完全相同的问题。一段时间以来,我一直在寻找一种方法来消除if if ifswitch阻止我的代码。

I opted for the Map+ Supplierapproach, implemented within a factory, as in the following snippet:

我选择了在工厂内实现的Map+Supplier方法,如下面的代码片段所示:

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):

我发现这个解决方案更接近开/关原则(SOLID 原则中的 O):

  1. Now, all i need to do to extend the functionalities is to add another operator class that implements some Operatorinterface that i have defined elsewhere, add it in the supportedOperatorsmap and in the SupportedOperatorsRegexwithout changing any of the business logic of the OperatorFactoryclass.
  2. Moreover, the client classes do not need to know what is happening in the OperatorFactory, and that is less coupling.
  1. 现在,为了扩展功能,我需要做的就是添加另一个运算符类,该类实现Operator我在别处定义的某些接口,将其添加到supportedOperators地图中,SupportedOperatorsRegex而不更改类的任何业务逻辑OperatorFactory
  2. 此外,客户端类不需要知道在 中发生了什么OperatorFactory,这减少了耦合。