从下面的代码片段中跳过太多if / else-if的更好方法是什么?


14

我正在尝试编写一个servlet,它根据传递给它的“操作”值执行任务。

这是其中的示例

public class SampleClass extends HttpServlet {
     public static void action1() throws Exception{
          //Do some actions
     }
     public static void action2() throws Exception{
          //Do some actions
     }
     //And goes on till action9


     public void doPost(HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException {
          String action = req.getParameter("action");

          /**
           * I find it difficult in the following ways
           * 1. Too lengthy - was not comfortable to read
           * 2. Makes me fear that action1 would run quicker as it was in the top
           * and action9 would run with a bit delay - as it would cross check with all the above if & else if conditions
           */

          if("action1".equals(action)) {
               //do some 10 lines of action
          } else if("action2".equals(action)) {
               //do some action
          } else if("action3".equals(action)) {
               //do some action
          } else if("action4".equals(action)) {
               //do some action
          } else if("action5".equals(action)) {
               //do some action
          } else if("action6".equals(action)) {
               //do some action
          } else if("action7".equals(action)) {
               //do some action
          } else if("action8".equals(action)) {
               //do some action
          } else if("action9".equals(action)) {
               //do some action
          }

          /**
           * So, the next approach i tried it with switch
           * 1. Added each action as method and called those methods from the swith case statements
           */
          switch(action) {
          case "action1": action1();
               break;
          case "action2": action2();
               break;
          case "action3": action3();
               break;
          case "action4": action4();
               break;
          case "action5": action5();
               break;
          case "action6": action6();
               break;
          case "action7": action7();
               break;
          case "action8": action8();
               break;
          case "action9": action9();
               break;
          default:
               break;
          }

          /**
           * Still was not comfortable since i am doing un-necessary checks in one way or the other
           * So tried with [reflection][1] by invoking the action methods
           */
          Map<String, Method> methodMap = new HashMap<String, Method>();

        methodMap.put("action1", SampleClass.class.getMethod("action1"));
        methodMap.put("action2", SampleClass.class.getMethod("action2"));

        methodMap.get(action).invoke(null);  

       /**
        * But i am afraid of the following things while using reflection
        * 1. One is Security (Could any variable or methods despite its access specifier) - is reflection advised to use here?
        * 2. Reflection takes too much time than simple if else
        */

     }
    }

我需要做的是避免在我的代码中进行过多的if / else-if检查,以提高可读性和代码维护性。因此尝试了其他替代方案,例如

1. 切换大小写 -在执行我的操作之前,它仍然进行了太多检查

2. 反思

i]主要是安全性-尽管有访问说明,但它甚至允许我访问类中的变量和方法-我不确定我是否可以在代码中使用它

ii],另一个是比简单的if / else-if检查需要更多时间

有没有更好的方法或更好的设计,有人建议以更好的方式组织上述代码?

已编辑

我已经添加了答案为上面的代码中考虑下面的答案

但是,以下类“ ExecutorA”和“ ExecutorB”仅执行几行代码。与将其添加为方法相比,将它们添加为类是一种好习惯吗?请在这方面提出建议。



2
为什么要用9个不同的操作重载单个servlet?为什么不简单地将每个动作映射到由不同servlet支持的不同页面?这样,动作的选择就由客户端完成,您的服务器代码仅专注于服务于客户端的请求。
Maybe_Factor

Answers:


13

根据先前的答案,Java允许枚举具有属性,因此您可以定义策略模式,例如

public enum Action {
    A ( () -> { //Lambda Sintax
        // Do A
       } ), 
    B ( () -> executeB() ), // Lambda with static method
    C (new ExecutorC()) //External Class 

    public Action(Executor e)
        this.executor = e;
    }

    //OPTIONAL DELEGATED METHOD
    public foo execute() {
        return executor.execute();
    }

    // Action Static Method
    private static foo executeB(){
    // Do B
    }
}

那么您的Executor(策略)将是

public interface Executor {
    foo execute();
}

public class ExecutorC implements Executor {
    public foo execute(){
        // Do C
    }
}

而且您的doPost方法中所有的if / else都会变成

public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
    String action = req.getParameter("action");
    Action.valueOf(action).execute();
}

这样,您甚至可以为枚举中的执行程序使用lambda。


说得好..但是我需要一点澄清..我所有的动作action1(),action2()都是几行代码..将其打包在一个类中会很好吗?
汤姆·泰勒

4
这不是说服您创建特定类/对象的行数,而是它们代表不同行为的事实。1个想法/概念= 1个逻辑对象。
mgoeminne

2
@RajasubaSubramanian如果您觉得类太重,也可以使用lambda或方法引用。Executor是(或可以是)功能接口。
绿巨人

1
@ J.Pichardo感谢您的更新:)因为我仍然在java7我不能使用lambda表达式。所以,接下来的战略格局的枚举实施建议在这里dzone.com/articles/strategy-pattern-implemented
汤姆·泰勒(Tom Taylor)

1
@RajasubaSubramanian很酷,我学到新东西,
J. Pichardo

7

不要使用反射,而要使用专用接口。

即代替:

      /**
       * Still was not comfortable since i am doing un-necessary checks in one way or the other
       * So tried with [reflection][1] by invoking the action methods
       */
      Map<String, Method> methodMap = new HashMap<String, Method>();

    methodMap.put("action1", SampleClass.class.getMethod("action1"));
    methodMap.put("action2", SampleClass.class.getMethod("action2"));

    methodMap.get(action).invoke(null);  

利用

 public interface ProcessAction{
       public void process(...);
 }

为每个操作实现它们中的每个,然后:

 // as attribute
Map<String, ProcessAction> methodMap = new HashMap<String, ProcessAction>();
// now you can add to the map you can either hardcode them in an init function
methodMap.put("action1",action1Process);

// but if you want some more flexibility you should isolate the map in a class dedicated :
// let's say ActionMapper and add them on init : 

public class Action1Manager{
    private static class ProcessAction1 implements ProcessAction{...}

    public Action1Manager(ActionMapper mapper){
       mapper.addNewAction("action1", new ProcessAction1());
    }
}

当然,这种解决方案并不是最轻巧的,因此您可能不需要花那么长时间。


我认为它必须是ProcessAction不是ActionProcess是这样的......?
汤姆·泰勒

1
是的,我已将其修复。
Walfrat

1
而且,更普遍的答案是“使用OOP机制”。因此,在这里,您应该对“状况”及其相关行为进行验证。换句话说,用一个抽象对象表示您的逻辑,然后操纵该对象而不是其基础的螺母和螺栓。
mgoeminne

同样,@ Walfrat提出的方法的自然扩展包括提出一个(抽象的)工厂,该工厂根据指定的String参数创建/返回正确的ProcessAction。
mgoeminne

@mgoeminne听起来不错
J. Pichardo

2

使用命令模式,这将需要一个命令界面,如下所示:

interface CommandInterface {
    CommandInterface execute();
}

如果Actions构建起来轻巧且便宜,则使用工厂方法。从映射的属性文件中加载类名,actionName=className并使用简单的工厂方法来构建要执行的动作。

    public Invoker execute(final String targetActionName) {
        final String className = this.properties.getProperty(targetAction);
        final AbstractCommand targetAction = (AbstractCommand) Class.forName(className).newInstance();
        targetAction.execute();
    return this;
}

如果Action的构建成本很高,请使用一个池,例如HashMap;但是在大多数情况下,我建议根据“ 单一责任原则”避免这种情况,将昂贵的元素委派给一些预先构建的公共资源池,而不是命令本身。

    public class CommandMap extends HashMap<String, AbstractAction> { ... }

然后可以使用

    public Invoker execute(final String targetActionName) {
        commandMap.get(targetActionName).execute();
        return this;
}

这是一种非常稳健且分离的方法,它采用了SOLID原则的 SRP,LSP和ISP 。新命令不会更改命令映射器代码。这些命令易于实现。可以将它们添加到项目和属性文件中。这些命令应该是可重入的,这使其非常有效。


1

您可以利用基于枚举的对象来减少对字符串值进行硬编码的需求。这将节省您一些时间,并使代码在将来阅读和扩展时变得更加整洁。

 public static enum actionTypes {
      action1, action2, action3....
  }

  public void doPost {
      ...
      switch (actionTypes.valueOf(action)) {
          case action1: do-action1(); break;
          case action2: do-action2(); break;
          case action3: do-action3(); break;
      }
  }

1

如果您正在寻找可扩展且维护性较差的设计,则我将使用Factory Method模式。

Factory Method模式定义了一个用于创建对象的接口,但是让子类决定要实例化哪个类。Factory Method使类将实例化延迟到子类。

 abstract class action {abstract doStuff(action)}

action1,action2 ........ actionN使用doStuff方法实现的具体实现。

刚打电话

    action.doStuff(actionN)

因此,如果将来采取更多的措施,您只需要添加具体的类即可。


错字缩写->第一行代码中的摘要。请编辑。另外,您是否可以添加更多代码来刷新此示例,以更直接地显示其如何回答OP的问题?
杰伊·埃斯顿

0

参考@J。皮卡多答案我正在写上面的代码段修改如下

public class SampleClass extends HttpServlet {

public enum Action {
    A (new ExecutorA()),
    B (new ExecutorB())

    Executor executor;

    public Action(Executor e)
        this.executor = e;
    }

    //The delegate method
    public void execute() {
        return executor.execute();
    }
}

public foo Executor {
    foo execute();
}

public class ExecutorA implements Executor{
   public void execute() {
      //Do some action
   }
}

public class ExecutorB implements Executor{
   public void execute() {
      //Do some action
   }
}

public void doPost(HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException {

  String action = req.getParameter("action"); 
  Action.valueOf(action).execute();
  }
}

如果动作太多,您是否会创建太多的类。我们有更好的实施方案吗?
Vaibhav Sharma
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.