Enquanto eu trabalhava recentemente em uma grande empresa, notei que os programadores seguiram esse estilo de codificação:
Suponha que eu tenha uma função que retorne 12 se a entrada for A, 21 se a entrada for B e 45 se a entrada for C.
Para que eu possa escrever a assinatura da função como:
int foo(String s){
if(s.equals("A")) return 12;
else if(s.equals("B")) return 21;
else if(s.equals("C")) return 45;
else throw new RuntimeException("Invalid input to function foo");
}
Mas, na revisão de código, fui solicitado a alterar a função para o seguinte:
int foo(String s){
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("A", 12);
map.put("B", 21);
map.put("C", 45);
return map.get(s);
}
Não consigo me convencer de por que o segundo código é melhor que o primeiro. O segundo código definitivamente levaria mais tempo para ser executado.
A única razão para usar o segundo código pode ser que ele oferece melhor legibilidade. Mas se a função estiver sendo chamada várias vezes, a segunda função não diminuiria o tempo de execução do utilitário que a chamava?
O que você pensa sobre isso?
fonte
switch
parece mais apropriado queif-else
). Mas, em algum momento, isso se torna problemático. A principal vantagem de usar um mapa é que você pode carregá-lo de um arquivo ou tabela, etc. Se você estiver codificando a entrada para o mapa, não estou vendo muito valor em um comutador.Respostas:
O objetivo é mover a criação do hashmap para fora da função e fazê-lo uma vez (ou apenas menos vezes do que o contrário).
No entanto, desde o java7, o java7 conseguiu seqüências de caracteres (finais) nos switches:
fonte
No seu segundo exemplo, o
Map
deve ser um membro estático privado para evitar sobrecarga de inicialização redundante.Para grandes quantidades de valores, o mapa terá um desempenho melhor. Usando uma hashtable, pode-se procurar a resposta em tempo constante. O construto múltiplo se precisa comparar a entrada com cada uma das possibilidades até encontrar a resposta certa.
Em outras palavras, a pesquisa de mapa é O (1) enquanto
if
s são O (n) onde n é o número de entradas possíveis.A criação do mapa é O (n), mas é feita apenas uma vez se for um estado constante estático. Para uma pesquisa realizada com freqüência, o mapa superará as
if
instruções a longo prazo, ao custo de um pouco mais de tempo quando o programa estiver iniciando (ou a classe for carregada, dependendo do idioma).Dito isto, o mapa nem sempre é a ferramenta certa para este trabalho. É bom quando existem muitos valores, ou os valores devem ser configuráveis por meio de um arquivo de texto, entrada do usuário ou banco de dados (nesse caso, o mapa atua como um cache).
fonte
Existem duas velocidades no software: o tempo necessário para escrever / ler / depurar o código; e o tempo necessário para executar o código.
Se você pode me convencer (e seus revisores de código) de que a função hashmap é realmente mais lenta que a if / then / else (depois de refatorar para criar um hashmap estático) E você pode me convencer / revisores de que é necessário tempo suficiente para fazer uma atualização real diferença, então vá em frente e substitua o hashmap pelo if / else.
Caso contrário, o código hashmap é eminentemente legível; e (provavelmente) livre de erros; você pode determinar isso rapidamente apenas olhando para ele. Você não pode realmente dizer a mesma coisa sobre o if / else sem realmente estudá-lo; a diferença é ainda mais exagerada quando existem centenas de opções.
fonte
Eu prefiro muito a resposta no estilo HashMap.
Há uma métrica para isso
Existe uma métrica de qualidade de código chamada Complexidade Ciclomática . Essa métrica conta basicamente o número de caminhos diferentes através do código ( como calcular a Complexidade Ciclomática ).
Para cada caminho de execução possível, um método se torna cada vez mais difícil de entender E testar completamente a correção.
Tudo se resume ao fato de que "controlar palavras-chave" como: ifs, elses, whiles, etc ... utiliza testes booleanos que podem estar errados. O uso repetido de "controlar palavras-chave" produz código frágil.
Benefícios adicionais
Além disso, a "abordagem baseada em mapa" incentiva os desenvolvedores a pensar nos pares de entrada e saída como um conjunto de dados que pode ser extraído, reutilizado, manipulado em tempo de execução, testado e verificado. Por exemplo, abaixo reescrevi "foo" para não ficarmos permanentemente presos em "A-> 12, B-> 21, C-> 45":
rachet_freak menciona esse tipo de refator em sua resposta, ele defende velocidade e reutilização, estou defendendo a flexibilidade do tempo de execução (embora o uso de uma coleção imutável possa ter enormes benefícios dependendo da situação)
fonte
Os dados são melhores que o código. Não menos importante, porque é muito tentador adicionar mais um ramo ao código, mas é difícil errar a adição de uma linha a uma tabela. Esta questão é uma pequena instância disso. Você está escrevendo uma tabela de pesquisa. Escreva uma implementação, completa com lógica e documentação condicionais, ou escreva a tabela e procure nela.
Uma tabela de dados é sempre uma representação melhor de alguns dados do que o código, pois a otimização do módulo passa. Quão difícil é a expressão da tabela pode depender da linguagem - eu não conheço Java, mas espero que possa implementar uma tabela de pesquisa de maneira mais simples do que o exemplo no OP.
Esta é uma tabela de pesquisa em python. Se isso é visto como um convite a conflitos, considere que a pergunta não está marcada como java, a refatoração é independente de idioma e que a maioria das pessoas não conhece java.
A idéia de reestruturar o código para reduzir o tempo de execução tem mérito, mas prefiro usar um compilador que eleva a configuração comum do que eu mesmo.
fonte