如何消除这个具体例子中的切换

时间:2018-01-21 07:07:33

标签: java oop design-patterns

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

现在我使用了switch,但我认为它打破了Open Closed Principle(因为每次添加新类型的主题我都要添加新的case来切换)而不是好的设计,我该如何重构这段代码呢?

 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 个答案:

答案 0 :(得分:4)

虽然我不确定这个代码片段违反了哪个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");
     ...
   }
}

OR

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

答案 1 :(得分:4)

// 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))代码混合使用。当您需要其他功能时,您可以在此地图中配置一个条目。

答案 2 :(得分:2)

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