Douglas ’ Blog

POO e Padrões de Projetos são lixo

~ 26 de fev. de 2024 ~


Quando comecei minha carreira em programação, aprendi que POO é uma ferramenta e, como toda ferramenta, tem seu lugar para resolver um problema.

A princípio parecia fazer sentido, mas à medida que fui adquirindo mais experiência em programação, percebi que POO não traz nenhum benefício. É por isso que escrevi para ignorar POO.

A primeira razão para ignorar OOP é que isso leva a um desempenho ruim:

Outra razão é que vários livros tentam corrigir POO:

Esses livros mostram que POO é falho visto que eles fornecem diretrizes para escrever melhor código OO. Todos os três livros combinados têm cerca de 1.200 páginas de heurísticas! Tente se lembrar de tudo isso!

Michael Feathers escreveu em seu blog dizendo que seu livro “Trabalho Eficaz com Código Legado” deveria se chamar “Trabalho Eficaz com Código Orientado a Objetos”.

Eu me pergunto por que não existe um livro para trabalhar com código funcional ou procedural legado. Provavelmente porque não existe uma técnica secreta, é muito simples de lidar, então não precisa de um livro para isso.

Outra razão é que linguagens modernas, como Zig, Go e Erlang, não adotam explicitamente a POO e não a incentivam. Algumas linguagens mais antigas, como Java, C# e C++, estão se afastando da POO através da implementação de recursos funcionais.

No meu primeiro post, mencionei que Rust não adota POO explicitamente, mas a documentação oficial mostra que sim. No entanto, parece que não é muito divulgado.

Alguns defensores da POO afirmam que a POO é mais adequada para softwares grandes e complexos. Esta afirmação é um absurdo completo. Existem muitos projetos não-OO grandes, complexos e bem-sucedidos: Git, Linux, Redis, Nginx, Postgres e SQLite. Você pode escrever software grande, complexo e bem-sucedido em qualquer paradigma, até mesmo assembly!

Outros defensores afirmam que POO é melhor para código de interface gráfica. Mas esta não é a única abordagem para escrever código de interface gráfica, como interface gráfica de modo imediato.

Uma das afirmações mais irritantes é quando alguém critica um código OO e diz que o desenvolvedor fez mais complicado do que precisa e não é culpa da POO. Os desenvolvedores podem projetar soluções excessivamente complicadas, mas acredito firmemente que POO leva a soluções excessivamente complicadas.

Para finalizar esta seção, gostaria de dar um exemplo onde o código procedural é muito mais simples que o código OO. Martin Fowler, que escreveu o livro Refactoring, reescreveu o exemplo da Loja de Vídeo em JavaScript, onde calcula a fatura do cliente e imprime o extrato.

Na primeira versão, ele escreveu em Java. O código inicial tem três classes e 88 linhas de código. Tudo isso cabe facilmente em 42 linhas de JavaScript. Se você adicionar tipagem, aumenta para 57, mas ainda menos que Java.

Você pode até traduzi-lo para C, que, apesar de ser mais antigo que Java, tem uma certa simplicidade que falta ao Java.


Já que já discuti que POO é lixo. Vamos para Padrões de Projeto. Como o título diz: “Padrões de Projetos: Soluções Reutilizáveis de Software Orientados a Objetos” é específico para POO.

O único propósito dos padrões de projeto é corrigir deficiências na linguagem, especialmente C++ e Java. Os autores concordam que alguns padrões não fazem sentido em outra linguagem porque possuem mecanismos melhores para resolver um problema. O trecho do livro:

…alguns de nossos padrões são suportados diretamente pelas linguagens orientadas a objetos menos comuns. O CLOS possui vários métodos, por exemplo, que diminuem a necessidade de um padrão como Visitor (página 331). Na verdade, existem diferenças suficientes entre Smalltalk e C++ para significar que alguns padrões podem ser expressos mais facilmente em uma linguagem do que em outra. e a maioria dessas linguagens existia antes de Java e C++, que são as linguagens alvo do livro.

Além disso, Peter Norvig apontou que “16 dos 23 padrões têm uma implementação qualitativamente mais simples em Lisp ou Dylan”. Outra postagem interessante mostra como Clojure simplifica esses padrões.

Se C++ ou Java tivessem sido baseados em Lisp, então o livro de padrões nunca existiria.

A escolha da linguagem de programação é importante porque influencia o ponto de vista de cada um. Nossos padrões assumem recursos de linguagem de nível Smalltalk/C++, e essa escolha determina o que pode e o que não pode ser implementado facilmente. Se assumissemos linguagens procedurais, poderíamos ter incluído padrões de design chamados “Herança”, “Encapsulação” e “Polimorfismo”.

Herança, encapsulamento e polimorfismo não são padrões! Eles são simplesmente recursos de linguagem. Eles não resolvem um problema recorrente.

Eu também acredito que Padrões de Projeto não descrevem problemas reais mas problemas fictícios da mentalidade de design da POO.

Algumas citações do livro explicam um pouco sobre o design da POO em geral.

A parte difícil do design orientado a objetos é decompor um sistema em objetos. A tarefa é difícil porque muitos fatores entram em jogo: encapsulamento, granularidade, dependência, flexibilidade, desempenho, evolução, capacidade de reutilização e assim por diante. Todos eles influenciam a decomposição, muitas vezes de formas conflitantes.

As metodologias de design orientadas a objetos favorecem muitas abordagens diferentes. Você pode escrever uma declaração de problema, destacar os substantivos e verbos e criar classes e operações correspondentes. Ou você pode se concentrar nas colaborações e responsabilidades do seu sistema. Ou você pode modelar o mundo real e traduzir os objetos encontrados durante a análise em design. Sempre haverá desacordo sobre qual abordagem é a melhor.

Esta é a falha fundamental do POO. Supõe que devemos projetar o código em torno de objetos, não da solução. Isso leva a um design estranho e a uma estrutura complexa, semelhante a este projeto satírico FizzBuzzEnterpriseEdition.

Os padrões de projeto resolvem muitos dos problemas diários enfrentados pelos designers orientados a objetos.

Eles enfrentam esses problemas porque POO é lixo.

A estrutura de tempo de execução de um programa orientado a objetos geralmente tem pouca semelhança com sua estrutura de código. A estrutura do código é congelada em tempo de compilação; consiste em classes em relacionamentos de herança fixa. A estrutura de tempo de execução de um programa consiste em redes de objetos em comunicação que mudam rapidamente. Na verdade, as duas estruturas são em grande parte independentes.

Com tanta disparidade entre as estruturas de tempo de execução e de compilação de um programa, fica claro que o código não revelará tudo sobre como um sistema funcionará. A estrutura de tempo de execução do sistema deve ser imposta mais pelo designer do que pela linguagem. Os relacionamentos entre objetos e seus tipos devem ser projetados com muito cuidado, pois determinam quão boa ou ruim é a estrutura de tempo de execução.

Esta é outra razão pela qual a OOP leva a um design ruim. Torna mais difícil entender o programa lendo-o porque é uma rede de objetos comunicantes.

Não consideramos esta coleção de padrões de projeto completa e estática; é mais uma gravação de nossos pensamentos atuais sobre design.

Então, por que não há uma segunda edição depois de 30 anos?

Para finalizar esta seção, refatorei um jogo simples. O jogo fez uso intenso do padrão observer. Então, como demonstração, removi as camadas indiretas e simplifiquei o código geral. No final, o padrão observer era desnecessário.

Você pode comparar você mesmo, o código original e minha versão refatorada. Então, veja qual é mais fácil de ler e entender.

Conclusão

POO tornou-se amplamente utilizado devido à popularidade de C++ e Java, não porque seja inerentemente bom. POO não oferece nenhum benefício que as pessoas dizem. O código procedural é tão flexível, modular e reutilizável quanto OO. A ideia de que POO é de alguma forma melhor nisso é um absurdo completo.

Tenha cuidado com qualquer pessoa que esteja “vendendo” padrões para você. Certifique-se de que eles sirvam a um propósito e resolvam seu problema. Não se esqueça de que as estruturas de dados e os algoritmos são muito mais fundamentais do que qualquer padrão de design.

A principal lição desta postagem é:

Se você ignorar POO, seu código ficará mais simples.

Para finalizar meu caso contra POO, selecionei uma lista de links que discutem um pouco sobre como a POO é ruim: