Como não utilizar IF ou SWITCH?

4 respostas
thiagoluisgarcia

Pessoal,

Gostaria de saber como posso fazer uma implementação simples (um exemplo) não utilizando IF ou SWITCH. Alguém pode me ajudar?

[]s
Thiago

4 Respostas

ViniGodoy

Olá, seja bem-vindo ao GUJ!
Quando tiver um tempo, leia esse artigo que vai te orientar a como usar alguns recursos do forum.

Esse assunto é muito estressado no livro refatoração, do Martin/Fowler. O princípio básico é substituir ifs e switchs por polimorfismo.

Vou mostrar com um exemplo com o padrão Strategy. Considere a classe:

public class TextFinder {
    private static final int BEGINS = 0;
    private static final int ENDS = 1;
    private static final int CONTAINS = 2;
    private static final int EQUALS = 3;
    private static final int REGEX = 4;
 
    public static boolean findText(String text, String expected, int criteria) {
       swtich (criteria) {
          case BEGINS:
             return text.startsWith(expected);
          case ENDS:
             return text.endsWith(expected);
          case CONTAINS:
             return text.contains(expected);
          case EQUALS:
             return text.equals(expected);
          case REGEX:
             Matcher matcher = Pattern.compile(expected).matcher(text);
             return matcher.find();
       }
    }
 }
Para usar esse método, o usuário faz algo como:
boolean containsGodoy = TextFinder.findText("Vinícius Godoy de Mendonça", "Godoy", TextFinder.CONTAINS);

Onde está o strategy nesse código? Bom, o criteria pode ser transformado num strategy. Podemos fazer isso de maneira simples, através de um enum:

public enum SearchCriteria {
     BEGINS {
         @Override public boolean doVerify(String text, String expected) {
             return text.substring(0, expected.length()).equals(expected);
         }
     },
     ENDS {
         @Override public boolean doVerify(String text, String expected) {
             return text.endsWith(expected);
         }
     },
     CONTAINS {
         @Override public boolean doVerify(String text, String expected) {
             return text.contains(expected);
         }
     },
     EQUALS {
         @Override public boolean doVerify(String text, String expected) {
             return text.equals(expected);
         }
     },
     REGEX {
         @Override public boolean doVerify(String text, String expected) {
             Matcher matcher = Pattern.compile(expected).matcher(text);
             return matcher.find();
         }
     };
 
     public final boolean verify(String text, String expected) {
         if (text == null)
             throw new IllegalArgumentException("Text cannot be null!");
         
         if (expected == null)
             throw new IllegalArgumentException("Expected text cannot be null!");
         
         return doVerify(text, expected);
     }
     
     public abstract boolean doVerify(String text, String expected);
 }
Agora, aquele nosso código com o switch seria trocado por:
public class TextFinder {
    public static boolean findText(String text, String expected, SearchCriteria criteria) {
        return criteria.verify(text, expected);
     }
 }
Para usar o novo método, o usuário fará:
boolean containsGodoy = TextFinder.findText("Vinícius Godoy de Mendonça", "Godoy", SearchCriteria.CONTAINS);

Note que o switch foi completamente eliminado.

Outra vantagem é que fossemos adicionar outro método a esse strategy (por exemplo, NOT_CONTAINS), teríamos que modificar apenas o enum, e não localizar no código todos os pontos onde o switch é utilizado.

Finalmente, você poderia ter mais de um método abstrato em cada item do strategy. Não é esse caso, mas se você tivesse um enum com algoritmos de criptografia, você poderia ter o encode e o decode. A grande vantagem é que, quando alguém fosse implementar um novo algoritmo, teria um "template" pronto de quais métodos ele deve fornecer. Assim, não há risco de alguém implementar um encode() sem pelo notar a necessidade de implementar um decode().

M

Olá …

Fiquei com uma dúvida … nesse não há os ConcreteStrategy??

Eles não são necessários para que se caracterize o padrão??

Grato

ViniGodoy

Cada item do enum é um ConcreteStrategy. Nesse caso, as inner classes anônimas que implementam o comportamento do Begin, Contains, Regex, Ends e Equals.

O AbstractStrategy é dado pela própria classe do enum, que tem um método virtual. No lugar desse método virtual, o Enum poderia implementar uma interface SearchStrategy, com esse método, o que daria na mesma (embora ficasse um pouco mais flexível).

Só não fiz isso pq não tinha porque complicar o exemplo. A idéia aqui era mostrar como eliminar o switch, não tanto dar uma aula sobre o padrão e seus detalhes.

M

Ok

Agradeço a atenção

Criado 29 de março de 2007
Ultima resposta 23 de out. de 2008
Respostas 4
Participantes 3