Portanto, provavelmente como muitos, muitas vezes me vejo enfrentando problemas de design nos quais, por exemplo, há algum padrão / abordagem de design que parece se encaixar intuitivamente no problema e tem os benefícios desejados. Muitas vezes, existem algumas ressalvas que dificultam a implementação do padrão / abordagem sem algum tipo de trabalho em torno do qual nega os benefícios do padrão / abordagem. Posso facilmente iterar através de muitos padrões / abordagens, porque previsivelmente quase todos eles têm algumas advertências muito significativas em situações do mundo real nas quais simplesmente não há uma solução fácil.
Exemplo:
Vou lhe dar um exemplo hipotético baseado vagamente em um exemplo real que encontrei recentemente. Digamos que eu queira usar a composição sobre a herança, porque as hierarquias de herança impediram a escalabilidade do código no passado. Eu posso refatorar o código, mas depois descobrir que há alguns contextos em que a superclasse / classe básica simplesmente precisa chamar a funcionalidade na subclasse, apesar das tentativas de evitá-lo.
A próxima melhor abordagem parece estar implementando um padrão de meio delegado / observador e meio de composição para que a superclasse possa delegar comportamento ou para que a subclasse possa observar eventos da superclasse. Em seguida, a classe é menos escalável e sustentável, porque não está claro como deve ser estendido; também é complicado estender os ouvintes / delegados existentes. Além disso, as informações não ficam ocultas porque é necessário conhecer a implementação para ver como estender a superclasse (a menos que você use comentários muito extensivamente).
Então, depois disso, posso optar por simplesmente usar completamente os observadores ou delegados para evitar as desvantagens que surgem com a mistura pesada das abordagens. No entanto, isso vem com seus próprios problemas. Por exemplo, eu acho que acabo precisando de observadores ou delegados para uma quantidade crescente de comportamentos até acabar precisando de observadores / delegados para praticamente todos os comportamentos. Uma opção pode ser ter apenas um grande ouvinte / delegado para todo o comportamento, mas a classe de implementação acaba com muitos métodos vazios, etc.
Então eu poderia tentar outra abordagem, mas há tantos problemas com isso. Em seguida, o próximo e o outro, etc.
Esse processo iterativo fica muito difícil quando cada abordagem parece ter tantos problemas quanto qualquer outro e leva a uma espécie de paralisia da decisão de design . Também é difícil aceitar que o código acabe igualmente problemático, independentemente de qual padrão ou abordagem de design seja usada. Se eu acabar nessa situação, isso significa que o problema em si precisa ser repensado? O que os outros fazem quando se deparam com essa situação?
Edit: Parece haver uma série de interpretações da pergunta que eu quero esclarecer:
- Tirei OOP da questão completamente porque, na verdade, ele não é específico para OOP, além de ser muito fácil interpretar erroneamente alguns dos comentários que fiz ao passar sobre OOP.
- Alguns afirmaram que eu deveria adotar uma abordagem iterativa e tentar padrões diferentes, ou que deveria descartar um padrão quando ele parar de funcionar. Este é o processo que pretendi referir em primeiro lugar. Eu pensei que isso estava claro no exemplo, mas eu poderia ter deixado mais claro, então editei a pergunta para fazer isso.