Mas quais problemas exatamente eles resolvem? Já que os exemplos do livro são muito teóricos. Além disso, não entendo a necessidade de 'catalogar' algo como o Strategy, e dizer que ele é um pattern, sendo que muito antes do livro ser escrito já se fazia uso das 'técnicas' que eu mencionei.

Sim, é teórico porque acredito que a ideia é ser uma descrição do "caso geral".

Ao codificar, vc verifica se tem uma situação similar e aí vê se tem algum pattern que pode te ajudar.

Por exemplo, eu preciso criar um tipo diferente conforme determinados parâmetros? Talvez usar factory seja uma opção. Eu preciso ter um algoritmo (um comportamento) diferente conforme cada situação? Deixa eu ver se usar strategy é uma boa. E assim por diante.

A ideia de catalogar - a meu ver, pois não sei se era a intenção original dos autores - é criar um vocabulário em comum, facilitando a comunicação. Em vez de descrever em detalhes o que é pra fazer ("crie uma classe/função/whatever que faça A, depois B e C, etc"), vc só diz que é pra usar o pattern X. Toda área tem vocabulário técnico e termos próprios, a nossa não é diferente.

Claro que em TI existe muito essa coisa de dar nomes novos pra coisas velhas, mas enfim... O fato é que agora esses nomes existem e se vc mencioná-los, muita gente vai entender do que se trata. Se precisava mesmo, sinceramente, acho que não faz mais tanta diferença. Mas agora que tem e todo mundo entende, não acho ruim usar.

> Por exemplo, eu preciso criar um tipo diferente conforme determinados parâmetros? Talvez usar factory seja uma opção. Eu preciso ter um algoritmo (um comportamento) diferente conforme cada situação? Deixa eu ver se usar strategy é uma boa. E assim por diante. Ambos os casos você consegue usar um `if`. Ainda não consigo ver como design patterns ajuda nesse caso.
Consegue sim e isso tbm e um strategy ou uma simple factory, só nao e do GOF. Complementando o que o amigo comentou anteriormente, tem vídeos no YouTubedo Alister Cockburn e do Uncle Bob descrevendo a soluçãodos design patterns exatamente como ele citou. Eram problemas comuns e precisavam chamar por algum nome, o que torna tudo mais simples. Os problemas ja existiam muito antes de alguem catalogar, o que fizeram foi padronizar e otimizar uma solução e que fazem sentido até hj e com um pouco de criatividade, talvez vc possa até melhorar a solução já existente porque não é um design fechado é uma proposta de caminho e não uma religião. Pensa assim, é mas simples vc dizer o nome de alguém ou descreve-la? O lance é, vc só vai entender a necessidade quando vc realmente tiver a dor e realmente entender a necessidade, antes disso estude e não se preocupe. Quando vc perceber, estará procurando uma solução mais manutenível para o seu problema robusto.
Acho que tem um detalhe que vc não entendeu: **usar `if` é uma das formas de implementar esses _patterns_**. Um *design pattern* só descreve de forma geral (teórica, genérica) o que é pra fazer. Mas **como** isso será feito fica a cargo de quem for implementar. Vc pode fazer com `if`, `switch`, polimorfismo, ponteiro de função ou seja lá o que for que a linguagem que está usando disponibiliza. Se no fim o código faz o que o *design pattern* descreve, então vc o usou - mesmo que ache que não :-) Claro que dá pra discutir qual solução é "melhor" de acordo com vários critérios arbitrários, mas isso vai além da definição do *pattern* (de novo: ele só diz o que deve ser feito, mas não como). E repito que o grande problema é que as implementações orientadas a objeto se tornaram tão populares que muita gente acha que é a única forma de usar DP. Ou pior, muitos acham que se não usar classes ou uma linguagem orientada a objeto (ou se "trocar por um `if`"), então não está usando DP. --- Só pra dar um exemplo prático (adaptado do [link que indiquei acima](https://www.adamtornhill.com/articles.htm), que por sua vez tem o link pra [este artigo](https://www.adamtornhill.com/Patterns%20in%20C%203,%20STRATEGY.pdf)). Vamos supor que um site de e-commerce tenha categorias diferentes de consumidor, e cada um tem uma faixa de desconto: clientes bronze têm 2% de desconto, clientes prata têm 5% e clientes ouro têm 10%. Então na hora de calcular o preço, eu preciso saber a categoria do cliente e aplicar o respectivo desconto. Ou seja, tem um comportamento diferente dependendo de cada caso. Ou, para ser mais técnico, eu posso selecionar um algoritmo diferente de cálculo de desconto em *runtime*, de acordo com determinados parâmetros (no caso, as condições - seja lá quais forem - que determinam a categoria de um cliente). E olha só, toda esta situação (precisar escolher um algoritmo/comportamento diferente em cada caso) tem um nome: *strategy*! Então eu poderia implementar com `if` ou `switch` (exemplos em JavaScript, sem nenhum motivo especial): ```javascript // com if function calcularPreco(categoria, valor) { if (categoria == 'bronze') { return valor * 0.98; // 2% de desconto } else if (categoria == 'prata') { return valor * 0.95; // 5% de desconto } else if (categoria == 'ouro') { return valor * 0.9; // 10% de desconto } // se não é nenhuma das categorias acima, não tem desconto return valor; } // ou com switch function calcularPreco(categoria, valor) { switch (categoria) { case 'bronze': return valor * 0.98; // 2% de desconto case 'prata': return valor * 0.95; // 5% de desconto case 'ouro': return valor * 0.9; // 10% de desconto default: // se não é nenhuma das categorias acima, não tem desconto return valor; } } ``` Qual desses é a implementação do *strategy*? **Ambos**! > "*Ah, mas o livro do GoF diz pra usar classes, interfaces, polimorfismo, blablabla*" E daí? E se eu estiver usando uma linguagem que não é orientada a objetos? No [link que indiquei](https://www.adamtornhill.com/articles.htm), ao final, tem vários links para artigos com implementações dos *patterns* em C (o exemplo acima foi adaptado [deste](https://www.adamtornhill.com/Patterns%20in%20C%203,%20STRATEGY.pdf)). **Mas aqui caímos em outra questão**, que vai além dos *patterns*. E talvez seja por isso que muita gente acha que só existe uma única forma de implementar cada um deles. Os exemplos acima têm alguns problemas (também citados no mesmo artigo). O principal - a meu ver - é que agora o cálculo de desconto está fortemente acoplado com as categorias. Se uma nova categoria surgir, eu preciso mudar o cálculo de preço. Indo mais além, vamos supor que existam outras coisas associadas à categoria. Por exemplo, o preço do frete pode ser diferente, os descontos podem aumentar de acordo com a quantidade de itens (e essa quantidade também varia para cada categoria), clientes ouro podem escolher mais parcelas, etc etc etc. E vamos supor que para cada uma dessas situações existe uma função com um `if` ou `switch`. Então se uma categoria nova é adicionada (ou alguma existente é removida, ou algum desses valores muda para uma delas), vc precisará mudar todas as funções associadas. O que era só um "simples `if`" se torna um pesadelo de manutenção. E como resolver? Uma solução é desacoplar as categorias dos respectivos cálculos. E é aí que surge a implementação "clássica" com classes: ```javascript class CategoriaBronze { calcularDesconto(valor) { return valor * 0.98; // 2% de desconto } } class CategoriaPrata { calcularDesconto(valor) { return valor * 0.95; // 5% de desconto } } class CategoriaOuro { calcularDesconto(valor) { return valor * 0.9; // 10% de desconto } } function calcularPreco(categoria, valor) { if (!categoria) // se não tem categoria, não tem desconto return valor; return categoria.calcularDesconto(valor); } ``` > Obs: claro que na implementação "clássica" existe uma interface (ou classe abstrata) `Categoria`, da qual todas as categorias herdam. Mas a ideia geral é essa. Desta forma, para a função `calcularPreco` tanto faz se eu criar, remover ou modificar alguma categoria, pois eu não preciso mais mudá-la. E no caso das categorias terem mais coisas (cálculo de desconto, de frete, benefícios específicos, etc), basta adicionar os métodos em cada uma. E cada função só recebe a categoria, e delega para ela os respectivos cálculos. Ou seja, em vez disso: ```javascript function calcularPreco(categoria, valor) { if (categoria == 'bronze') { return valor * 0.98; // 2% de desconto } else if (categoria == 'prata') { return valor * 0.95; // 5% de desconto } else if (categoria == 'ouro') { return valor * 0.9; // 10% de desconto } // se não é nenhuma das categorias acima, não tem desconto return valor; } function maximoParcelas(categoria) { // clientes ouro podem escolher mais parcelas if (categoria == 'ouro') { return 20; } return 10; } ``` Eu poderia ter isso: ```javascript // aqui eu mudei para ter uma classe base da qual todas herdam class Categoria { // por padrão, todas as categorias podem parcelar em até 10 vezes maxParcelas() { return 10; } calcularDesconto(valor) { return valor; // por padrão, não tem desconto } } class CategoriaBronze extends Categoria { calcularDesconto(valor) { return valor * 0.98; // 2% de desconto } } class CategoriaPrata extends Categoria { calcularDesconto(valor) { return valor * 0.95; // 5% de desconto } } class CategoriaOuro extends Categoria { calcularDesconto(valor) { return valor * 0.9; // 10% de desconto } maxParcelas() { return 20; // clientes ouro podem parcelar em mais vezes } } function calcularPreco(categoria, valor) { if (!categoria) // se não tem categoria, não tem desconto return valor; return categoria.calcularDesconto(valor); } function maximoParcelas(categoria) { if (!categoria) return 5; return categoria.maxParcelas(); } ``` No primeiro código, se eu mudar alguma categoria (seja adicionando, removendo ou modificando uma existente), precisarei verificar todas as funções (`calcularPreco` e `maximoParcelas`). E vamos supor que o sistema tem mais trocentas funções para tratar de diferentes aspectos relacionados à categoria (cada uma tem um preço de frete diferenciado, ofertas especiais em itens específicos, brindes, etc etc etc). Se eu usar `if`/`switch`, cada alteração nas categorias implica em ter que revisar o código de todas essas funções. Já se usar o segundo código, eu não preciso alterar as funções `calcularPreco` e `maximoParcelas` (e nem todas as outras trocentas funções que mencionei). Claro que ainda vou ter que testar tudo, mas pelo menos eu não precisei revisar o código de todas elas pra saber qual precisa ser modificada (em caso de adicionar ou remover uma categoria, por exemplo, eu teria que mexer em todas se usasse `if`). "_Ah, então só dá pra fazer com classes?_". Claro que não. Menciono novamente o [artigo](https://www.adamtornhill.com/Patterns%20in%20C%203,%20STRATEGY.pdf) que explica como fazer em C, e ele usa ponteiros de função. --- Ou seja, se vc só "trocar por `if`", ainda estará implementando o *pattern*. Mas existem outras questões que vão além desta simples troca, como a dificuldade de manutenção em um código com alto acomplamento. Talvez por isso muita gente associe o *pattern* com "não use `if`", ou ache que se usar `if` não está implementando o *pattern*. Está sim, mas talvez não seja da melhor forma, por causa desses problemas de manutenção.
```js const categorias = { bronze: { maxParcelas: 2, }, prata: { desconto: 0.95, maxParcelas: 4, }, ouro: { desconto: 0.98, maxParcelas: 20, }, diamante: { desconto: 0.10, }, } function calcularPreco(categoria, valor) { return valor * (categorias[categoria]?.desconto || 1); } function calcularParcelas(categoria) { return categorias[categoria]?.maxParcelas || 10; } ``` Pronto, agora fica fácil adicionar categorias e funções.
Sim, esta é uma das várias implementações possíveis de *strategy*, com todas as vantagens que este *pattern* traz. O problema é que vc ainda está preso à ideia de que DP é só sobre orientação a objetos, e que só dá pra fazer com classes. Não é, e novamente deixo o link para o [artigo](https://www.adamtornhill.com/Patterns%20in%20C%203,%20STRATEGY.pdf) que mostra como implementar em C (sem usar classes e orientação a objeto, portanto). E segue também os demais links que indiquei em outro comentário: - [Non-OOP Design Patterns?](https://softwareengineering.stackexchange.com/q/67594) - [Is Design Pattern only for Object-Oriented design?](https://stackoverflow.com/q/2898366) - [How is OOP and Design Patterns related?](https://stackoverflow.com/q/478773) O que acontece é que o livro do GoF se tornou tão popular que fez com que muita gente achasse que DP e OO são a mesma coisa, ou que só dá pra fazer o primeiro usando o segundo.
> Acho que tem um detalhe que vc não entendeu: usar if é uma das formas de implementar esses patterns. > Um design pattern só descreve de forma geral (teórica, genérica) o que é pra fazer. Mas como isso será feito fica a cargo de quem for implementar. > Vc pode fazer com if, switch, polimorfismo, ponteiro de função ou seja lá o que for que a linguagem que está usando disponibiliza. Isso não faz o menor sentido. Design patterns é, e sempre será sobre orientação a objetos. Só porque eu uso `if/switch` não significa que estou usando o design pattern. > E daí? E se eu estiver usando uma linguagem que não é orientada a objetos? No link que indiquei, ao final, tem vários links para artigos com implementações dos patterns em C (o exemplo acima foi adaptado deste). Ele apenas demonstra como "implementar" o pattern em C, não que usar `if/switch` **é** o design pattern. Não é só porque dá para "implementar" em C que significa que seja algo universal. > Se eu usar if/switch, cada alteração nas categorias implica em ter que revisar o código de todas essas funções. Você está 'otimizando' para um cenário absurdo, em que terá _trocentas_ funções. Além disso você superestima o esforço gasto pra adicionar uma nova categoria na versão com `if/switch`. > Já se usar o segundo código, eu não preciso alterar as funções calcularPreco e maximoParcelas (e nem todas as outras trocentas funções que mencionei). Mas se precisar adicionar uma função `calcularFrete(categoria)` em que cada categoria tem um cálculo de frete diferente, você vai precisar alterar em todas as classes. E no caso do `if/switch`, é só adicionar a nova função de frete.
> *Design patterns é, e sempre será sobre orientação a objetos* Não é, tanto que em outro comentário indiquei este link: [Non-OOP Design Patterns?](https://softwareengineering.stackexchange.com/q/67594). E novamente eu destaco o primeiro comentário que tem lá: > *I think the biggest disservice the popular design pattern books did was create a whole slew of people that believed the patterns only apply to object oriented languages.* Em tradução livre (com ênfase minha): > *Acho que o maior desserviço dos livros que popularizaram os design patterns foi que **eles fizeram uma quantidade enorme de pessoas acreditarem que os patterns só se aplicam a linguagens orientadas a objeto**.* Que parece ser exatamente o que está acontecendo aqui :-) # E pra quem negativou, sugiro dar uma olhada também [aqui](https://stackoverflow.com/q/2898366) e [aqui](https://stackoverflow.com/q/478773) pra entender que *design pattern* não é sobre orientação a objeto. --- Quanto ao restante, é sempre um *trade-off* que deve ser avaliado caso a caso. Se o sistema for relativamente simples, não justifica a complexidade de ter classes e toda aquela parafernalha, por exemplo. Tanto que há muitos que defendem - e eu concordo - que as vantagens da orientação a objeto só aparecem em projetos complexos, com bases de código bem grandes. Se não é o seu caso, se acha que não precisa, então não use. Eu já precisei refatorar um código que começou simples (`if`/`switch` atendia bem), mas foi ficando complexo e viu-se vantagem em mudar pra algo como o exemplo acima (a manutenção nos anos seguintes ficou bem mais fácil). > *Mas se precisar adicionar uma função calcularFrete(categoria) em que cada categoria tem um cálculo de frete diferente, você vai precisar alterar em todas as classes. E no caso do if/switch, é só adicionar a nova função de frete.* Não necessariamente. Por exemplo, se o frete só é diferenciado pra categoria ouro, e pro resto é igual, eu só preciso criar na classe base (com o valor padrão) e na classe ouro (com o valor diferente). E mesmo que tenha que adicionar em todas as classes, será feito apenas uma vez. Depois, se o frete mudar para alguma categoria, ou se eu criar uma categoria nova ou remover uma existente, não preciso mais mexer na função (se fosse com `if`, eu teria que mexer sempre nela). Mas como já disse, cada caso é um caso. Se as regras não mudam tanto assim, aí tanto faz. Ninguém disse que é obrigatório usar DP. Mas eles podem ser úteis se souber quando e como usar.
No mundo Pascal/Delphi tem um termo bastante famoso, "if bom é if morto". Uso excessivo de If aumenta a complexidade do código e o auto acoplamento. Conforme frase retirada do livro "If bom é if morto" do Thulio Bittercourt: > Quando o programador recorre frequentemente a instruções IF para controlar o fluxo do programa, ele pode inadvertidamente aumentar a complexidade cognitiva, exigindo que quem lê o código mantenha múltiplas possíveis ramificações e condições em mente. > Esse aumento na carga mental não apenas dificulta a compreensão do código, mas também eleva o risco de erros, uma vez que fica mais complicado garantir a cobertura de todos os caminhos durante os testes.
https://stackoverflow.com/a/1167628
If em excesso é exatamente isso, programador ruim. Edit: Ou melhor dizendo, programador inexperiente.

Eles resolvem os problemas que estão nos livros. E não são complexos de maneira geral, talvez pouco didáticos. O que ocorre é que hoje vc não tem a real necessidade de usar e possívelmente não tem experiência suficiente para identificar os lugares onde estes padrões se encaixariam. Não que eu saiba de todos tbm kkkkkkk

Mas para te ajudar vou te sugerir um conteudo https://refactoring.guru/pt-br.

Ai nesse site tem exemplos mais didáticos para que vc possa adquirir a experiência necessária para conseguir reconhecer os padrões e saber como usá-los