Unixtopia
main/ artigos/
Programação Orientada a Objetos
Quando inventei o termo orientado a objetos, C++ não era o que eu tinha em mente — Alan Kay
A programação obcecada por objetos é um paradigma que tenta modelar a realidade como uma coleção de objetos abstratos que se comunicam entre si e obedecem a algumas regras. Embora a ideia em si não seja ruim e possa ser útil em certos casos, se tornou usada em excesso, mal implementada e totalmente forçada em linguagens que aplicam essa abstração a cada conceito, criando antipadrões e problemas desnecessários. Portanto, a POO é um câncer do desenvolvimento de software. Exemplos incluem Java e C++. Outras linguagens como Python e JavaScript incluem POO, mas o tornaram um pouco mais leve e pelo menos permitem que você evite usá-lo. Você deve aprender POO, mas apenas para ver por que ela é uma praga, e para realmente entender 99% do código escrito hoje em dia.
Princípios
Tenha em mente que POO não tem uma definição única. Ela assume muitas formas e mutações dependendo da linguagem e é praticamente sempre combinada com outros paradigmas, como o imperativo. Geralmente, programas POO resolvem problemas tendo objetos que se comunicam entre si. Cada objeto é especializado para fazer alguma coisa, e.g, um lida com o texto, outro com cache, outro com renderização de imagens. Cada objeto tem seus dados e métodos, funções próprias do objeto, por exemplo, humano pode fornecer métodos getHeight, drinkBeer ou petCat. Os objetos podem enviar mensagens uns aos outro,: por exemplo, um objeto humano envia uma mensagem a outro objeto humano para obter seu nome, na prática, isso significa que o primeiro objeto chama um método do outro objeto, assim como chamamos funções, como em human2.getName()).
Agora, linguagens POO usam classes. Nelas, definimos classes de objetos, uma classe é um modelo para um objeto, ela define métodos e tipos de dados para armazenar. Qualquer objeto que criamos é criado com base em alguma classe, por exemplo, criamos o objeto Mr. Unix e Lady C da classe Humano, assim como normalmente criamos uma variável x e y do tipo int. Dizemos que um objeto é uma instância de uma classe, i.e, objeto é uma manifestação real do que uma classe descreve, com dados específicos. O tipo mais leve de POO é chamado de POO sem classes, que é baseado em ter os chamados objetos protótipos em vez de classes. Podemos simplesmente criar objetos sem classes e então atribuir a eles propriedades e métodos dinamicamente em tempo de execução. Aqui, em vez de criar uma classe Human, criamos um objeto protótipo que serve como um modelo para outros objetos. Para criar humanos específicos, clonamos o protótipo humano e modificamos o clone. A POO vem com alguns princípios básicos.
- Encapsulamento: o objeto não deve ser capaz de acessar os dados de objetos diretamente, eles podem usar apenas seus métodos. Um objeto não deve ser capaz de acessar o atributo de altura de um objeto Humano, ele deve ser capaz de acessá-lo somente por meio de métodos desse objeto, como getHeight. Isso leva ao antipadrão setter e getter.
- Polimorfismo: objetos diferentes de classes diferentes podem ter métodos com o mesmo nome que se comportam de forma diferente para cada objeto e podemos simplesmente chamar esse método sem nos importarmos com o tipo de objeto, a implementação correta é escolhida em tempo de execução. Objetos das classes Humano e Bomba podem ter o método setOnFire, que com o primeiro matará o humano e com o último causará uma explosão matando muitos humanos. Isso é bom em um caso em que temos uma matriz de componentes GUI e queremos executar, por exemplo, redimensionar em cada um deles, simplesmente iteramos sobre toda a matriz e chamamos o método redimensionar em cada objeto sem nos importarmos se o objeto é um botão, caixa de seleção ou janela.
- Herança: as classes formam uma hierarquia na qual as classes pai podem ter classes filhas, Uma classe LivingBeing terá subclasses Humano e Animal. Subclasses herdam coisas da classe pai e podem adicionar mais algumas. No entanto, isso leva a antipadrões. A herança é hoje considerada ruim até mesmo por programadores modernos e está sendo substituída pela composição.
Por que é uma praga?
- É uma abstração para muitos problemas que, por natureza, não são orientados a objetos. Não é uma solução, mas tenta se comportar como uma. O maior problema é que ela tenta resolver tudo. Ela força a ideia de que dados e algoritmos devem sempre vir juntos, mas isso é uma afirmação estúpida.
- Para programas simples como muitos utilitários UNIX, POO é desnecessário. Fazendo você lutar contra restrições artificiais em vez de se concentrar em resolver o problema em questão. Grande número de supostos recursos e padrões de design, setters/getters, singletons, herança, acabaram sendo, na verdade, antipadrões e fardos, esta não é uma afirmação controvers.
- Como qualquer abstração mais alta, muitas vezes vem com sobrecarga, consumo de memória e perda de desempenho, bem como compiladores mais complexos, especificações de linguagem e dependências.
- É um híbrido de paradigma imperativo e POO que apenas ocupam mais espaço na cabeça, criam atrito e problemas desnecessários. Linguagens sensatas agora permitem a escolha de usar POO totalmente, parcialmente ou evitá-lo completamente, o que leva a uma supercomplicação dois em um.
- A ideia ingênua de POO de que o mundo real é composto de objetos bem definidos, como Humanos e Árvores, é completamente errada, em vez disso, vemos merdas como AbstractIntVisitorShitFactory.
- A ideia de que POO levaria à reutilização de código falhou, o código de implementação de classes específicas é normalmente sobrecarregado com dependências internas e externas, OOPers acreditavam que seu paradigma criaria um mundo cheio de caixas reutilizáveis, mas esse não era o caso, não é necessário para caixas, nem a prática mostrou que contribuiria para isso, muito pelo contrário, por exemplo, bibliotecas C de headers são muito mais reutilizáveis.
- Bons programadores não precisam de POO porque sabem programar, POO não inventa nada, é apenas uma maneira de tentar forçar uma programação principalmente em programadores incompetentes contratados por empresas, para evitar que causem danos, já que não sabem programar. Isso claramente não funciona, um programador merda sempre programará merda, ele encontrará seu caminho para foder apesar de quaisquer obstáculos e se você inventar obstáculos bons o suficiente para impedi-lo de foder, você também o impedirá de programar algo que funcione bem enquanto você amarra suas mãos.
- Programadores bons também escrevem códigos ruins e com bugs, mas isso é mais um sintoma de um design industrial ruim, aqui a POO está tentando curar sintomas de uma direção inerentemente errada, não está abordando a causa raiz. A POO apenas repete o que módulos já fazem.
- Se você quer programar de forma orientada a objetos e tem uma boa justificativa para isso, não precisa de uma linguagem POO, você pode emular todos os aspectos em linguagens simples como C. Em vez de construir a ideia na própria linguagem e arrastá-la para sempre e em todos os lugares, seria melhor ter bibliotecas opcionais.
- Ela generaliza e simplifica a programação em regras práticas, como encapsulamento, novamente para o bem de novatos inexperientes. Não há regras simples sobre como programar bem, uma boa programação requer uma grande quantidade de experiência e, como em qualquer arte, um bom programador sabe quando quebrar regras gerais é bom. POO não permite que bons programadores façam isso, ela prega coisas como variáveis globais são ruins, o que é simplificado e prejudicial.
Qual paradigma usar em vez de POO?
Depois que perceberam que POO é uma merda, houve diversas alternativas, como funcional e programação orientada a agentes. Então qual usar? Imperativo, também chamado de procedimental. Lembre-se de que isso não quer dizer que você nunca deve aplicar um paradigma diferente, mas o imperativo deve ser padrão, o mais prevalente e adequado para usar na solução da maioria dos problemas. Por que imperativo?
- Por que não podemos simplesmente melhorar a POO ou inventar algo ultra genial para substituí-la? A resposta é que o paradigma imperativo é especial porque é como os computadores realmente funcionam, não é inventado, mas sim o paradigma natural de baixo nível com abstração mínima que reflete a natureza dos computadores. Você pode dizer que isso é racionalização arbitrária de merda, mas essas propriedades tornam o paradigma imperativo especial entre todos os outros paradigmas.
- Sua implementação é simples e sem problemas porque mapeia bem e naturalmente para o hardware subjacente, comandos em uma linguagem simplesmente traduzem para uma ou mais instruções. Isso torna a construção de compiladores fácil. É previsível e eficiente, um programador escrevendo código imperativo pode ver claramente como o que ele está escrevendo será traduzido para as instruções de montagem. Isso torna o código altamente eficiente, ao contrário de paradigmas high-level que realizam enormes quantidades de mágica para traduzir conceitos estrangeiros para instruções de máquina, essa mágica pode diferir entre compiladores, i.e, o que é código eficiente em um compilador pode ser ineficiente em outro, como o OpenGL, onde a implementação do driver começou a desempenhar papel enorme e levou à criação de uma API Vulkan de nível mais baixo.
- Não força abstração desnecessária high-level. Significa que podemos usar qualquer abstração, até POO, se precisarmos dela no momento, por meio de uma biblioteca, mas não somos forçados a usar conceitos estranhos em problemas que não podem ser descritos facilmente em termos desses conceitos. Se você estiver resolvendo um problema com POO, você desperdiça esforço em traduzir esse problema para POO e o compilador então desperdiça outro esforço para traduzir isso para instruções. Com o paradigma imperativo isso não pode acontecer porque você está basicamente escrevendo instruções que precisam acontecer de qualquer maneira.
- Quanto maior a abstração, menor deve ser seu escopo de aplicação, então a abstração padrão deve ser de baixo nível. Isso funciona na ciênciaz a psicologia é uma abstração de alto nível, mas só pode ser aplicada para estudar o comportamento humano, enquanto a física quântica é uma abstração de baixo nível que se aplica ao universo.
- Uma vez que computadores começam trabalhar em paradigma diferente, podemos mudar para esse paradigma como padrão, mas até lá o imperativo é o caminho a seguir.