有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何在这个特定示例中消除开关

我有一个控制器方法,它从请求中获取数据,并根据请求中的主题变量决定调用函数。(对于项目需要,我不能对每个主题变量使用单独的控制器方法)

现在我使用了switch,但我认为它打破了开闭原则(因为每次添加新类型的主题时,我都必须为switch添加新的案例),而且设计不好,如何重构代码

 Subject subject = ... //(type of enum)
 JSONObject data = request.getData("data");

switch(subject) {

    case SEND_VERIFY:
       send_foo1(data.getString("foo1_1"), data.getString("foo1_2"));
       break;

    case do_foo2:
       foo2(data.getInt("foo2_b"), data.getInt("foo2_cc"));
       break;

    case do_foo3:
       do_foo3_for(data.getString("foo3"));
       break;

    // some more cases

 }

共 (3) 个答案

  1. # 1 楼答案

    虽然我不确定这段代码违反了哪个OO原则,但确实有一种更为激进的方法来实现逻辑:将每个枚举值的处理绑定到枚举类

    您需要将处理概括为一个接口:

    public interface SubjectProcessor
    {
       void process(JSONObject data);
    }
    

    并为每个枚举值创建具体实现:

    public class SendVerifySubjectProcessor implements SubjectProcessor
    {
       @Override
       public void process(JSONObject data) {
         String foo1 = data.getString("foo1_1");
         String foo2 = data.getString("foo1_2");
         ...
       }
    }
    

    一旦有了类层次结构树,就可以将每个枚举值与具体的处理器相关联

    public enum Subject
    {
       SEND_VERIFY(new SendVerifySubjectProcessor()),
       do_foo2(new Foo2SubjectProcessor()),
       ...
       private SubjectProcessor processor
    
       Subject(SubjectProcessor processor) {
         this.processor = processor;
       }
    
       public void process(JSONObject data) {
         this.processor.process(data);
       }
    }
    

    这样就不需要在控制器中使用switch语句:

     Subject subject = ... //(type of enum)
     JSONObject data = request.getData("data");
     subject.process(data);
    

    编辑:

    根据好的注释,您可以使用java.util.function.Consumer函数接口,而不是自定义的SubjectProcessor接口。您可以决定是编写具体的类还是使用lambda expr构造

    public class SendVerifySubjectProcessor implements Consumer<JSONObject>
    {
       @Override
       public void accept(JSONObject data) {
         String foo1 = data.getString("foo1_1");
         String foo2 = data.getString("foo1_2");
         ...
       }
    }
    

    或者

    public enum Subject
    {
       SEND_VERIFY(data -> {
         String foo1 = data.getString("foo1_1");
         String foo2 = data.getString("foo1_2");
         ...
       }),
    
       ...
       private Consumer<Subject> processor
    
       Subject(Consumer<Subject> processor) {
         this.processor = processor;
       }
    
       public void process(JSONObject data) {
         this.processor.accept(data);
       }
    }
    
  2. # 2 楼答案

    其他人的答案似乎很好,作为补充,我建议使用EnumMap将枚举存储为键,因为它可能比标准Map更有效。我认为还值得一提的是,策略模式在这里用于实现从Map调用每个键的特定操作,而无需构建长切换语句

  3. # 3 楼答案

    // SubjectsMapping.java
    Map<Subject, Consumer<JSONObject>> tasks = new HashMap<>();
    tasks.put(SEND_VERIFY, 
        data -> send_foo1(data.getString("foo1_1"), data.getString("foo1_2")));
    tasks.put(do_foo2, 
        data -> foo2(data.getInt("foo2_b"), data.getInt("foo2_cc")));
    tasks.put(do_foo3, data -> do_foo3_for(data.getString("foo3")));
    
    
    // In your controller class where currently `switch` code written
    if (tasks.containsKey(subject)) {
        tasks.get(subject).accept(data);
    } else {
        throw new IllegalArgumentException("No suitable task");
    }
    

    您可以在单独的类中维护Map<Subject, Consumer<JSONObject>> tasks配置,而不是与if (tasks.containsKey(subject))代码混合使用。当您需要其他功能时,可以在此地图中配置一个条目