Eu tenho um aplicativo que leva um número inteiro como entrada e com base na entrada chama métodos estáticos de diferentes classes. Sempre que um novo número é adicionado, precisamos adicionar outro caso e chamar um método estático diferente de uma classe diferente. Agora existem 50 casos no comutador e toda vez que preciso adicionar outro caso, estremeço. Existe uma maneira melhor de fazer isso.
Pensei um pouco e tive essa ideia. Eu uso o padrão de estratégia. Em vez de ter um caso de opção, eu tenho um mapa de objetos de estratégia com a chave como o número inteiro de entrada. Depois que o método é chamado, ele pesquisará o objeto e chamará o método genérico para o objeto. Dessa forma, posso evitar o uso da construção de caso de switch.
O que você acha?
fonte
switch
caso e chamar um método preexistente em seu sistema complexo ou precisa inventar o método e sua chamada?Respostas:
Eu amo polimorfismo. Eu amo o SOLID. Eu amo pura programação orientada a objetos. Eu odeio ver esses dados com um péssimo representante, porque eles são aplicados dogmaticamente.
Você não fez um bom argumento para refatorar a estratégia. A refatoração tem um nome a propósito. Chama-se Substituir Condicional pelo Polimorfismo .
Encontrei alguns conselhos pertinentes para você no site c2.com :
Você tem um switch com 50 caixas e sua alternativa é produzir 50 objetos. Oh e 50 linhas de código de construção do objeto. Isto não é progresso. Por que não? Como essa refatoração não faz nada para reduzir o número de 50. Você usa essa refatoração quando descobrir que precisa criar outra instrução de opção na mesma entrada em outro lugar. É quando essa refatoração ajuda porque transforma 100 em 50 novamente.
Desde que você esteja se referindo à "opção" como se fosse a única que você possui, não recomendo isso. A única vantagem resultante da refatoração agora é que ela reduz as chances de que alguns bobões copiem e colem sua chave de 50 casos.
O que eu recomendo é examinar atentamente esses 50 casos em busca de pontos em comum que podem ser levados em consideração. Quero dizer 50? Realmente? Tem certeza de que precisa de tantos casos? Você pode estar tentando fazer muito aqui.
fonte
Um mapa de objetos de estratégia sozinho, que é inicializado em alguma função do seu código, em que você tem várias linhas de código parecidas com
requer que você e seus colegas implementem as funções / estratégias a serem chamadas em classes separadas, de uma maneira mais uniforme (já que todos os seus objetos de estratégia terão que implementar a mesma interface). Esse código geralmente é um pouco mais abrangente do que
No entanto, ele ainda não o liberará do fardo de editar esse arquivo de código sempre que um novo número precisar ser adicionado. Os benefícios reais dessa abordagem são diferentes:
a inicialização do mapa agora se separa do código de despacho que realmente chama a função associada a um número específico, e o último não contém mais essas 50 repetições, apenas parecerá
myMap[number].DoIt(someParameters)
. Portanto, esse código de despacho não precisa ser tocado sempre que um novo número chegar e pode ser implementado de acordo com o princípio Aberto-Fechado. Além disso, quando você obtém requisitos nos quais precisa estender o próprio código de despacho, não precisará mais alterar 50 locais, mas apenas um.o conteúdo do mapa é determinado em tempo de execução (enquanto o conteúdo da construção do comutador é determinado antes do tempo de compilação), portanto, você tem a oportunidade de tornar a lógica de inicialização mais flexível ou extensível.
Portanto, sim, existem alguns benefícios, e este é certamente um passo em direção a mais código SOLID. Se vale a pena refatorar, no entanto, é algo que você ou sua equipe terão que decidir por si mesmos. Se você não espera que o código de expedição seja alterado, a lógica de inicialização seja alterada e a legibilidade do arquivo
switch
não seja um problema real, sua refatoração poderá não ser tão importante agora.fonte
Doit1
,Doit2
, etc, com umDoit
método que tem muitas implementações diferentes.doTheThing()
um método para o símbolo de entrada. Então você não precisa manter o mapa.Sou totalmente a favor da estratégia descrita na resposta do @DocBrown .
Vou sugerir uma melhoria para a resposta.
As chamadas
pode ser distribuído. Você não precisa voltar ao mesmo arquivo para adicionar outra estratégia, que adere ao princípio Aberto-Fechado ainda melhor.
Digamos que você implemente
Strategy1
no arquivo Strategy1.cpp. Você pode ter o seguinte bloco de código nele.Você pode repetir o mesmo código em todos os arquivos StategyN.cpp. Como você pode ver, haverá muito código repetido. Para reduzir a duplicação de código, você pode usar um modelo que pode ser colocado em um arquivo acessível a todas as
Strategy
classes.Depois disso, a única coisa que você deve usar no Strategy1.cpp é:
A linha correspondente no StrategyN.cpp é:
Você pode levar o uso de modelos para outro nível usando um modelo de classe para as classes concretas da Strategy.
E então, em vez de
Strategy1
, useConcreteStrategy<1>
.Altere a classe auxiliar para registrar
Strategy
s para:Altere o código em Strateg1.cpp para:
Altere o código no StrategN.cpp para:
fonte