Estou tentando escrever um servlet que executa tarefas com base no valor "action" passado como entrada.
Aqui está a amostra da qual
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
*/
}
}
Tudo que eu preciso é escapar de muitas verificações if / else-if no meu código para melhor legibilidade e melhor manutenção do código. Então tentei outras alternativas como
1. caso de mudança - ainda faz muitas verificações antes de executar minha ação
2. reflexão
i] uma coisa principal é a segurança - que me permite acessar até as variáveis e métodos dentro da classe, apesar de seu especificador de acesso - não tenho certeza se o tempo poderia ser usado no meu código
ii] e o outro é que leva tempo mais do que as verificações simples de se / outra coisa se
Existe alguma abordagem melhor ou melhor design que alguém possa sugerir para organizar o código acima de uma maneira melhor?
EDITADO
Eu adicionei a resposta para o snippet acima, considerando a resposta abaixo .
Mas ainda assim, as seguintes classes "ExecutorA" e "ExecutorB" executam apenas algumas linhas de código. É uma boa prática adicioná-los como classe do que adicioná-lo como método? Por favor, informe a este respeito.
fonte
Respostas:
Com base na resposta anterior, o Java permite que as enums tenham propriedades para que você possa definir um padrão de estratégia, algo como
Então sua
Executor
(Estratégia) seriaE todo o seu if / else no seu
doPost
método se torna algo comoDessa forma, você pode usar lambdas para os executores nas enumerações.
fonte
Executor
é (ou pode ser) uma interface funcional.Em vez de usar a reflexão, use uma interface dedicada.
ou seja, em vez de:
Usar
Implementa cada um deles para cada ação e, em seguida:
É claro que essa solução não é a mais leve, portanto, talvez você não precise ir até esse ponto.
fonte
ProcessAction
vez deActionProcess
é assim ...?Use o padrão de comando , isso exigirá uma interface de comando mais ou menos assim:
Se eles
Actions
são leves e baratos de construir, use um Método de Fábrica. Carregue os nomes de classe de um arquivo de propriedades que mapeieactionName=className
e use um método simples de fábrica para criar as ações para execução.Se as ações são caras para construir, use um pool, como um HashMap ; no entanto, na maioria dos casos, eu sugeriria que isso poderia ser evitado sob o Princípio da Responsabilidade Única, delegando o elemento caro a algum pool de recursos comuns pré-construído, em vez dos próprios comandos.
Estes podem ser executados com
Essa é uma abordagem muito robusta e dissociada que aplica SRP, LSP e ISP dos princípios do SOLID . Novos comandos não alteram o código do mapeador de comandos. Os comandos são simples de implementar. Eles podem ser adicionados apenas ao projeto e ao arquivo de propriedades. Os comandos devem ser reentrantes e isso o torna muito eficiente.
fonte
Você pode utilizar o objeto baseado em enumeração para reduzir a necessidade de codificar permanentemente os valores da sequência. Isso poupará algum tempo e tornará o código muito interessante de ler e estender no futuro.
fonte
O padrão do Método de Fábrica é o que eu vejo se você estiver procurando por um design escalável e menos sustentável.
O padrão Factory Method define uma interface para criar um objeto, mas permite que a subclasse decida qual classe instanciar. O método Factory permite que uma classe adie a instanciação para a subclasse.
action1, action2 ........ actionN implementação concreta com o método doStuff implementando o que fazer.
Apenas ligue
Portanto, no futuro, se mais ação for introduzida, você só precisará adicionar uma classe concreta.
fonte
Com referência a @J. Resposta Pichardo, estou escrevendo a modificação do trecho acima, como a seguir
fonte