Quais são as maneiras de eliminar o uso da opção no código?
design-patterns
Mariano
fonte
fonte
Respostas:
As instruções de comutação não são um antipadrão por si só, mas se você estiver codificando orientação a objeto, deve considerar se o uso de uma opção é melhor resolvido com polimorfismo, em vez de usar uma instrução de comutação.
Com o polimorfismo, isso:
torna-se o seguinte:
fonte
typeof
, e esta resposta não sugere maneiras ou motivos para contornar as instruções switch em outras situações.Veja o cheiro das instruções do switch :
A refatoração e a refatoração de padrões têm abordagens para resolver isso.
Se o seu (pseudo) código se parecer com:
Este código viola o Princípio Aberto Fechado e é frágil para cada novo tipo de código de ação que aparece. Para remediar isso, você pode introduzir um objeto 'Comando':
Se o seu (pseudo) código se parecer com:
Então você pode introduzir um objeto 'State'.
Espero que isto ajude.
fonte
Map<Integer, Command>
não precisaria de uma opção?Um switch é um padrão, implementado com uma instrução switch, caso contrário, cadeia, tabela de pesquisa, polimorfismo oop, correspondência de padrões ou qualquer outra coisa.
Deseja eliminar o uso da " instrução switch " ou do " padrão switch "? O primeiro pode ser eliminado, o segundo, apenas se outro padrão / algoritmo puder ser usado, e na maioria das vezes isso não é possível ou não é uma abordagem melhor para fazê-lo.
Se você deseja eliminar a instrução switch do código, a primeira pergunta a fazer é onde faz sentido eliminar a instrução switch e usar alguma outra técnica. Infelizmente, a resposta a esta pergunta é específica do domínio.
E lembre-se de que os compiladores podem fazer várias otimizações para alternar instruções. Portanto, por exemplo, se você deseja processar as mensagens com eficiência, uma instrução switch é praticamente o caminho a percorrer. Por outro lado, a execução de regras de negócios com base em uma instrução switch provavelmente não é o melhor caminho a ser percorrido, e o aplicativo deve ser pesquisado novamente.
Aqui estão algumas alternativas para mudar de instrução:
fonte
Mudar por si só não é tão ruim, mas se você tiver muitos "switch" ou "if / else" nos objetos em seus métodos, pode ser um sinal de que seu design é um pouco "processual" e que seus objetos são apenas valor baldes. Mova a lógica para seus objetos, invoque um método em seus objetos e deixe-os decidir como responder.
fonte
Eu acho que a melhor maneira é usar um bom mapa. Usando um dicionário, você pode mapear quase qualquer entrada para algum outro valor / objeto / função.
seu código ficaria assim (psuedo) assim:
fonte
Todo mundo adora ENORME
if else
blocos. Tão fácil de ler! Estou curioso para saber por que você deseja remover as instruções do switch. Se você precisar de uma instrução switch, provavelmente precisará de uma instrução switch. Sério, eu diria que depende do que o código está fazendo. Se tudo o que o switch está fazendo é chamar funções (digamos), você pode passar ponteiros de função. Se é uma solução melhor , é discutível.A linguagem é um fator importante aqui também, eu acho.
fonte
Eu acho que o que você está procurando é o Padrão de Estratégia.
Isso pode ser implementado de várias maneiras, que foram mencionadas em outras respostas a esta pergunta, como:
fonte
switch
seria bom substituir as instruções se você adicionar novos estados ou novo comportamento às instruções:Adicionar novo comportamento requer copiar
switch
e adicionar novos estados significa adicionar outrocase
a cadaswitch
instrução.Em Java, você pode alternar apenas um número muito limitado de tipos primitivos cujos valores você conhece no tempo de execução. Isso apresenta um problema por si só: os estados estão sendo representados como números ou caracteres mágicos.
A correspondência de padrões e vários
if - else
blocos podem ser usados, embora realmente tenham os mesmos problemas ao adicionar novos comportamentos e novos estados.A solução sugerida por outros como "polimorfismo" é uma instância do padrão do Estado :
Substitua cada um dos estados por sua própria classe. Cada comportamento tem seu próprio método na classe:
Cada vez que você adiciona um novo estado, é necessário adicionar uma nova implementação da
IState
interface. Em umswitch
mundo, você adicionaria umcase
a cada umswitch
.Cada vez que você adiciona um novo comportamento, é necessário adicionar um novo método à
IState
interface e a cada uma das implementações. Essa é a mesma carga de antes, embora agora o compilador verifique se você tem implementações do novo comportamento em cada estado preexistente.Outros já disseram que isso pode ser muito pesado, então é claro que há um ponto em que você chega onde se move de um para outro. Pessoalmente, a segunda vez que escrevo um switch é o ponto em que refatoro.
fonte
se-mais
Eu refuto a premissa de que o interruptor é inerentemente ruim.
fonte
Bem, por um lado, eu não sabia que usar o interruptor era um anti-padrão.
Em segundo lugar, a opção sempre pode ser substituída por instruções if / else if.
fonte
Por que você quer? Nas mãos de um bom compilador, uma instrução switch pode ser muito mais eficiente do que blocos if / else (além de ser mais fácil de ler), e somente os maiores switches provavelmente serão acelerados se forem substituídos por qualquer tipo da estrutura de dados de pesquisa indireta.
fonte
'switch' é apenas uma construção de linguagem e todas as construções de linguagem podem ser pensadas como ferramentas para realizar um trabalho. Como nas ferramentas reais, algumas são mais adequadas a uma tarefa do que a outra (você não usaria uma marreta para colocar um gancho de imagem). A parte importante é como é definido 'fazer o trabalho'. Ela precisa ser mantida, precisa ser rápida, precisa ser dimensionada, precisa ser extensível e assim por diante.
Em cada ponto do processo de programação, geralmente há uma variedade de construções e padrões que podem ser usados: um comutador, uma sequência if-else-if, funções virtuais, tabelas de salto, mapas com ponteiros de função e assim por diante. Com a experiência, um programador saberá instintivamente a ferramenta certa a ser usada em uma determinada situação.
Deve-se supor que qualquer pessoa que mantenha ou revise o código seja pelo menos tão experiente quanto o autor original, para que qualquer construção possa ser usada com segurança.
fonte
Se a opção existe para distinguir entre vários tipos de objetos, provavelmente você está perdendo algumas classes para descrever com precisão esses objetos ou alguns métodos virtuais ...
fonte
Para C ++
Se você está se referindo a um AbstractFactory, acho que um método registerCreatorFunc (..) geralmente é melhor do que precisar adicionar um caso para cada declaração "nova" necessária. Em seguida, deixe todas as classes criarem e registrarem uma função creator (..) que pode ser facilmente implementada com uma macro (se ouso mencionar). Eu acredito que essa é uma abordagem comum que muitos frameworks fazem. Vi pela primeira vez no ET ++ e acho que muitas estruturas que exigem uma macro DECL e IMPL o utilizam.
fonte
Em uma linguagem processual, como C, o switch será melhor do que qualquer uma das alternativas.
Em uma linguagem orientada a objetos, quase sempre existem outras alternativas disponíveis que utilizam melhor a estrutura do objeto, particularmente o polimorfismo.
O problema com as instruções do comutador surge quando vários blocos de comutadores muito semelhantes ocorrem em vários locais do aplicativo e o suporte a um novo valor precisa ser adicionado. É muito comum um desenvolvedor esquecer de adicionar suporte ao novo valor a um dos blocos de switch espalhados pelo aplicativo.
Com o polimorfismo, uma nova classe substitui o novo valor e o novo comportamento é adicionado como parte da adição da nova classe. O comportamento nesses pontos de comutação é herdado da superclasse, substituído para fornecer um novo comportamento ou implementado para evitar um erro do compilador quando o super método é abstrato.
Onde não existe um polimorfismo óbvio, pode valer a pena implementar o padrão da estratégia .
Mas se sua alternativa for um grande bloco IF ... THEN ... ELSE, esqueça-o.
fonte
Use um idioma que não vem com uma instrução de switch interna. Perl 5 vem à mente.
Sério, por que você quer evitá-lo? E se você tem um bom motivo para evitá-lo, por que não simplesmente evitá-lo?
fonte
Os ponteiros de função são uma maneira de substituir uma enorme declaração de switch robusta; eles são especialmente bons em idiomas onde você pode capturar funções por seus nomes e fazer coisas com elas.
Obviamente, você não deve forçar as instruções do switch a sair do seu código, e sempre há uma chance de você estar fazendo tudo errado, o que resulta em trechos redundantes de código estúpidos. (Isso é inevitável às vezes, mas um bom idioma deve permitir que você remova a redundância enquanto permanece limpo.)
Este é um ótimo exemplo de divisão e conquista:
Digamos que você tenha algum intérprete.
Em vez disso, você pode usar isto:
Observe que não sei como remover a redundância da tabela opcode_table em C. Talvez eu deva fazer uma pergunta sobre isso. :)
fonte
A resposta mais óbvia, independente da linguagem, é usar uma série de 'se'.
Se o idioma que você está usando tiver ponteiros de função (C) ou funções com valores de 1ª classe (Lua), você poderá obter resultados semelhantes a uma "opção" usando uma matriz (ou uma lista) de funções (ponteiros para).
Você deve ser mais específico no idioma, se quiser melhores respostas.
fonte
As instruções de chave geralmente podem ser substituídas por um bom design de OO.
Por exemplo, você tem uma classe de conta e está usando um extrato de opção para executar um cálculo diferente com base no tipo de conta.
Eu sugeriria que isso fosse substituído por várias classes de contas, representando os diferentes tipos de conta, e todas implementando uma interface de Conta.
A mudança se torna desnecessária, pois você pode tratar todos os tipos de contas da mesma forma e, graças ao polimorfismo, o cálculo apropriado será executado para o tipo de conta.
fonte
Depende do motivo pelo qual você deseja substituí-lo!
Muitos intérpretes usam 'gotos computados' em vez de instruções de chave para execução de código de operação.
O que sinto falta no switch C / C ++ é o Pascal 'in' e os intervalos. Eu também gostaria de poder ativar as cordas. Mas estes, embora sejam triviais para um compilador comer, são trabalhosos quando feitos usando estruturas, iteradores e outras coisas. Então, pelo contrário, há muitas coisas que eu gostaria de substituir por um switch, se apenas o switch de C () fosse mais flexível!
fonte
O switch não é um bom caminho a percorrer, pois quebra o Principal Principal Aberto. É assim que eu faço.
E aqui está como usá-lo (usando seu código):
Basicamente, o que estamos fazendo é delegar a responsabilidade à classe filho, em vez de pedir aos pais que decidam o que fazer com os filhos.
Você também pode ler sobre o "Princípio de substituição de Liskov".
fonte
Em JavaScript, usando matriz associativa:
this:
torna-se o seguinte:
Cortesia
fonte
Outro voto para if / else. Eu não sou um grande fã de declarações de caso ou troca, porque existem algumas pessoas que não as usam. O código é menos legível se você usar maiúsculas ou minúsculas. Talvez não seja menos legível para você, mas para aqueles que nunca precisaram usar o comando.
O mesmo vale para fábricas de objetos.
Os blocos if / else são uma construção simples que todos recebem. Há algumas coisas que você pode fazer para garantir que não cause problemas.
Em primeiro lugar - não tente recuar as declarações mais de um par de vezes. Se você está se identificando, está fazendo errado.
É muito ruim - faça isso em seu lugar.
Otimização seja condenada. Não faz muita diferença na velocidade do seu código.
Em segundo lugar, não sou avesso a interromper um bloco If, desde que haja instruções de interrupção suficientes espalhadas pelo bloco de código específico para torná-lo óbvio
EDIT : No Switch e por que eu acho difícil grunhir:
Aqui está um exemplo de uma instrução switch ...
Para mim, a questão aqui é que as estruturas de controle normais que se aplicam em linguagens como C foram completamente quebradas. Existe uma regra geral de que, se você quiser colocar mais de uma linha de código dentro de uma estrutura de controle, use chaves ou uma instrução begin / end.
por exemplo
Para mim (e você pode me corrigir se eu estiver errado), a instrução Case lança essa regra pela janela. Um bloco de código executado condicionalmente não é colocado dentro de uma estrutura de início / fim. Por isso, acredito que Case seja conceitualmente diferente o suficiente para não ser usado.
Espero que responda às suas perguntas.
fonte