Afinal de contas, qual a definição de Orientação a Objetos?

Terminal Coffee - Sep 5 - - Dev Community

Introdução

O paradigma da orientação a objetos certamente é um dos tópicos mais relevantes da programação, sendo encontrada em diversas das boas práticas da industria como SOLID, GoF Design Patterns, ou Object Calhistenics, e até mesmo em linguagens extremamente populares como Java, C#, Ruby, PHP, JavaScript, etc. E dada tamanha relevância, ela também é alvo de diversos debates tanto em discussões online ou até na própria acadêmia.

Uma das principais polêmicas em torno do paradigma seria: o que de fato seria a orientação a objetos? Visto que ninguém concorda com o que ela significa, sendo esse o cerne da questão em vários tipos de brigas de internet como por exemplo quando alguém clama que a sua linguagem é verdadeiramente orientada a objetos enquanto as outras não são, ou que determina linguagem não é realmente orientada a objetos, e etc.

O que pode parecer bobo a primeira vista, mas existe um problema escondido atrás disso que é o fato de realmente não existir uma definição formal para o que seria a orientação a objetos. Logo, ao se criticar algo relacionado com o paradigma não podemos realmente ter certeza se ela é justa ou não devido ao fato de ninguém saber do que a verdade se trata de fato, já que isso só vai levar a um debate onde ambas as partes podem acusar a outra de atacar um espantalho visto que elas não concordam com a definição do que elas estão debatendo, o que as leva a falar sobre coisas diferentes enquanto pensam falar sobre a mesma coisa.

Isso acontece pois ela não nasce de uma base formal que pode ser identificada como fonte primária das discussões tal como o paradigma estruturado/procedural - que nasce do teorema Boehm-Jacopini e do artigo Go-to statement considered harmful - ou do funcional que nasce do cálculo lambda, pelo contrário, tecnicamente, ela tem duas história de origens diferentes, o que dificulta muito as coisas, afinal é complicado até mesmo dizer quem foi a primeira linguagem orientada a objetos, se foi Simula ou o Smalltalk.

Se você não tem contato prévio com o tema, pode estar achando tudo o que eu estou descrevendo aqui um tanto quanto estranho, afinal, todos aprendemos sobre orientação a objetos, usamos a palavra para nos comunicarmos diariamente, e tudo parece seguir normalmente, sem ruídos na comunicação, não é mesmo?

Pode até estar pensando que é muito fácil visto que mesmo se olharmos em fontes diferentes como um tutorial genérico na internet, a Wikipedia, ou até se passarmos pelo ensino formal (como um curso técnico ou faculdade), vamos encontrar definições bem semelhantes à algo que eu chamo de: os 4 pílares da orientação a objetos:

  • Abstração;
  • Encapsulamento;
  • Herança;
  • Polimorfismo.

Só que isso na verdade só conta uma parte da história, pois como apontei anteriormente, na verdade, existem duas escolas diferentes de pensamento orientado a objetos, uma que cresceu baseada na tradição criada no Simula, e desenvolvida e popularizada pelo C++, e outra que cresceu na tradição criada pelo Smalltalk.

Mesmo que possamos aceitar o lado vencedor da história na tradição do C++ (visto que a grande maioria das linguagens consideradas OO atualmente seguem mais pelas suas ideias do que pelas de Smalltalk), ou seja, do legado de Simula que evoluiu nos 4 pílares, não se tem um consenso sobre se eles são exatamente esses 4 ou se realmente deveriam ser 4. Por exemplo:

  • O próprio criador da linguagem C++ considera uma definição que só possui 3 pílares (abstração, herança, e polimorfismo);
  • A linguagem Eiffel considera outro conjunto de 6 princípios (classes, asserções, genericidade, herança, polimorfismo, e dinamyc binding);
  • O criador da linguagem Smalltalk diz que o que ele quis dizer ao cunhar o termo não é nada disso que usamos atualmente, e descreve que na verdade se resume a 3 princípios (Troca de mensagens, encapsulamento, e Late-binding extremo de todas as coisas);
  • A linguagem Ada define apenas 3 princípios (encapsulamento, herança, e polimorfismo);
  • A linguagem Self se baseia em 3 ideias(protótipos, slots, e comportamentos) que apesar de terem outros nomes, são equivalentes a termos usados em outras definições (herança, campos, e troca de mensagens);
  • A linguagem Java define 5 conceitos (encapsulamento, campos, herança, métodos, e objetos);
  • A linguagem C# segue os 4 pílares;
  • O criador da linguagem Simula admite não existir uma definição concreta, mas cita alguns conceitos que acredita serem comuns as linguagens consideradas orientadas a objeto (objetos, classes, herança, polimorfismo);
  • A linguagem Python cita 4 conceitos como orientados a objeto (classes, objetos, herança, e polimorfismo);
  • A linguagem Ruby afirma seguir a definição de Smalltalk, com grande enfase nas ideias de que tudo deveria ser um objeto, e troca de mensagens;
  • A linguagem PHP não possui uma definição oficial sobre orientação a objetos, mas podemos derivar que ela segue algo como os 4 pílares com base nas funcionalidades que ele apresenta como orientadas a objeto;
  • A linguagem Objective-C não possui uma definição oficial sobre orientação a objetos, mas seu manual sugere a seguinte combinação de conceitos (tudo é um objeto, classes, troca de mensagens, encapsulamento, herança, e polimorfismo);
  • O site MDN, em sua seção sobre a linguagem JavaScript considera 3 conceitos (classes e objetos, herança e encapsulamento);
  • Grady Booch, autor do livro Object-Oriented Analysis and Design With Applications, define 7 princípios (abstração, encapsulamento, modularidade, hierarquia, tipagem, concorrência, e persistência);
  • A GoF (Gang of Four: Erich Gamma, Richard Helm, Ralph Johnson, e John Vlissides), em seu livro Design Patterns : Elements of Reusable Object-Oriented Software cita encapsulamento, herança e polimorfismo como "padrões de projeto" numa linguagem procedural;
  • Peter Van Roy, em seu livro Concepts, Techiniques, and Models of Computer Programming, descreve OO em 4 items (objetos como mecanismo de abstração, estado explícito, polimorfismo, e herança);
  • A ISO/IEC 2382 item 2122503, que é o mais próximo que a gente tem de uma definição formal, considera 6 princípios (abstração, encapsulamento, herança, polimorfismo, troca de mensagens, e dynamic binding);
  • Yegor Bugayenko, define por meio de um modelo formal criado por ele chamado de phi-calculus, implementado na sua linguagem EOLANG;
  • etc...

Definindo uma perspectiva

Dessa forma, assim como Bjarne Stroustrup (criador do C++) argumenta em sua definição de orientação a objetos:

Qualquer definição de "orientação a objetos" deveria ser históricamente aceitável. Palavras são apenas úteis para comunicação, elas apenas significiam alguma coisa, se concordarmos em um significado para elas.

~ Bjarne Stroustrup, adaptado do inglês em tradução livre

Por isso gostaria de adotar uma abordagem holística (analisando pelo todo ao invés de pela soam das partes) para analisar o que deve ser considerado para a minha definição. Nisso avaliarei de acordo com 4 critérios em ordem de importância:

  1. Alinhamento conceitual com a perspectiva definida;
  2. Relevância histórica;
  3. Implementações na prática (que também será considerada a percepção no senso comum);
  4. Exclusividade para a OO (o quanto o conceito pode ser identificado como algo de orientação a objetos, ao invés de algo de paradigma X ou Y, ou um conceito mais geral);

Vamos começar por definir o nosso ponto de partida pela história, primeiro vamos decidir qual dos lados vamos escolher como fonte primária, e então seguir com as intenções do que deveria significar orientação a objetos dos autores dela como base para nossa definição, além de usa-la como parâmetro de comparação para julgar qual conjunto de princípios faz sentido a ser enquadrado nela.

Nessa questão, eu gosto da visão de que Alan Kay inventou a "orientação a objetos", mas não os "objetos", por isso nossa base vai ser a visão dele sobre o que deveria ser a orientação a objetos, que podemos resumir em algumas citações:

Apenas um gentil lembrete de que me esforcei no último OOPSLA para tentar lembrar a todos que Smalltalk não é apenas sua sintaxe ou biblioteca de classes, nem se trata de classes. Lamento ter cunhado há muito tempo o termo "objetos" para este tópico porque faz com que muitas pessoas se concentrem no ideia menor.

A grande ideia é "mensagens" - é isso que o núcleo do Smalltalk/Squeak é tudo sobre...

~ Alan Kay, adaptado do inglês em tradução livre

Pensei em objetos como células biológicas e/ou individuais computadores em uma rede, capazes apenas de se comunicar por meio de mensagens (então mensagens vieram logo no início - demorou um pouco para ver como enviar mensagens em uma linguagem de programação com eficiência suficiente para ser útil).

Eu queria me livrar dos dados. O B5000 quase fez isso através de seu arquitetura de HW quase inacreditável. Eu percebi que o A metáfora da célula/computador inteiro eliminaria os dados, e esse "<-" seria apenas mais um token de mensagem (demorei um pouco para pense nisso porque eu realmente pensei em todos esses símbolos como nomes para funções e procedimentos.

Minha formação em matemática me fez perceber que cada objeto poderia ter várias álgebras associadas a ele, e poderia haver famílias de estes, e que estes seriam muito úteis. O termo o "polimorfismo" foi imposto muito mais tarde (acho que por Peter Wegner) e não é muito válido, pois na verdade vem da nomenclatura de funções, e eu queria um pouco mais do que funções. Eu inventei um termo "genericidade" para lidar com comportamentos genéricos em um forma quase algébrica.

Não gostei da maneira como o Simula I ou o Simula 67 fizeram a herança (embora eu pensasse que Nygaard e Dahl eram apenas tremendos pensadores e designers). Então decidi deixar de fora a herança como uma funcionalidade built-in até que eu entendesse melhor.

~ Alan Kay, adaptado do inglês em tradução livre

POO para mim significa apenas troca de mensagens, retenção local dos dados e proteção e ocultamento do estado, e extremo late binding de todas as coisas.

~ Alan Kay, adaptado do inglês em tradução livre

Com isso podemos concluir que a perspectiva pelo qual vemos um programa orientado a objetos usando as definições de Kay, é a seguinte:

Um programa é visto como uma rede de objetos se comunicando por meio de troca de mensagens, no qual se busca programar evitando os dados, focando nas interações, de forma que uma mensagem possa carregar vários significados.

Falando em perspectiva, esse é a seção utilizado pelos criadores de Simula em seu livro Object-Oriented Programming in the BETA Programming Language para descrever o framework conceitual de um framework, ou seja, uma descrição de como enxergamos a estrutura de um programa escrito em determinado paradigma, por exemplo no caso da programação procedural, eles a descrevem como:

A execução de um programa é considerada uma sequência (parcialmente ordenada) de chamadas de procedimento que manipulam variáveis.

~ Nygaard at all, adaptado do inglês em tradução livre

No caso da funcional:

Um programa é considerado uma função matemática, que descreve uma relação entre entrada e saída.

~ Nygaard at all, adaptado do inglês em tradução livre

E para a orientação a objetos:

A execução de um programa é considerada um modelo físico, simulando o comportamento de uma parte real ou imaginária do mundo.

~ Nygaard at all, adaptado do inglês em tradução livre

Assim, mesmo que nosso foco seja na definição de Alan Kay, dado as grandes contribuições de Krysten Nygaard e Ole-Johan Dahl (criadores do Simula) no que diz respeito as funcionalidades encontradas em linguagens orientadas a objeto, e em quão bem sua perspectiva envelheceu ao longo do tempo, visto que quase todo tutorial moderno ainda segue essa história que os objetos representam conceitos do mundo real, sendo até formalizado como parte da definição de abstração, eu diria que é adequado incorporar suas visões na nossa definição final.

Dessa forma, onde puder ser feito um argumento em favor da conciliação das duas tradições numa definição unificada, assim respeitando nossa abordagem de uma análise holística, eu tentarei.

Por isso, como ela não é necessariamente exclusiva a nossa definição atual, podemos incrementar ela com o seguinte:

Um programa é visto como uma rede de objetos, sendo estes representações de conceitos do domínio, se comunicando por meio de troca de mensagens, no qual se busca programar evitando os dados, focando nas interações, de forma que uma mensagem possa carregar vários significados. Sendo sua estrutura geral algo semelhante a uma simulação do comportamento de uma parte real ou imaginária do mundo.

Uma consideração que pode ser feita é que isso pode direcionar as pessoas a focarem na "ideia menor", mas assim como admitimos antes é necessário reconhecer como as ideias evoluem ao longo do tempo pois uma palavra só tem valor se as pessoas entendem o que ela quer comunicar, portanto eu acredito que esta perspectiva encontra balanço o suficiente entre manter os objetivos iniciais de Alan Kay, e incorporar os valores do sistema de objetos de Nygaard e Dahl.

Definindo conceitos

Com o texto atual da definição, eu já poderia me dar por satisfeito, sendo essa a minha resposta a questão apresentada neste artigo, mas eu acredito que uma definição completa de um paradigma deveria incluir tanto a sua "perspectiva" do que representa a execução de um programa, quanto um conjunto de princípios associados a ela.

Por isso, podemos dizer que estamos na metade do caminho, e agora podemos voltar à aquela lista extensa de definições que eu dei mais cedo para buscar quais princípios se adequam a nossa perspectiva ao mesmo tempo que não traem a nossa visão base (o foco na troca de mensagens).

Para isso, novamente, vamos voltar a nossa fonte primária, os princípios da OO do Smalltalk:

  • Troca de mensagens;
  • Retenção local dos dados, junto com proteção e ocultamento do estado;
  • Extremo late binding.

Se usarmos o critério de relevância histórica de novo, poderia ser argumentado que como esses termos nem se usam mais hoje (com exceção de troca de mensagens), por isso deveríamos considerar usar os 4 pílares no lugar, e, novamente, acredito que usar uma abordagem conciliadora aqui seria o melhor dos dois mundos sem contradições:

  • Troca de mensagens;
  • Polimorfismo;
  • Encapsulamento;
  • Abstração;
  • Herança;

Existe uma explicação para a escolha desses 5 princípios é que:

Troca de mensagens

A troca de mensagens é algo inegociável dentro do contexto original da OO, afinal Alan Kay afirma ser a grande ideia do paradigma, seu conceito mais importante, por isso todos os outros conceitos devem ter algo a ver com ela.

Sua adoção na prática é bem considerável até, visto que linguagens com relevância histórica como Self, Objective-C, e Ruby mantém uma forte conexão com este conceito, e o próprio Ruby é considerado uma linguagem mainstream nos dias de hoje, com grandes aplicações construídas nele como o próprio github, além de ter uma comunidade bem ativa.

E eu diria que junto com herança e encapsulamento, é um dos conceitos que mais carrega a identidade da OO, visto que as outras duas instâncias (piada proposital) onde o termo é usado seria no Actor Model, que é um modelo matemático formal de lógica que basicamente tem quase as mesmas ideias de Alan Kay, mas é totalmente baseado em concorrência (algo como se OO fosse 100% async para vocês dev JS entenderem).

Polimorfismo

Esse é um conceito que acerta todos os critérios de nossa análise, visto que assim como notado por Nygaard e Dahl, ele está presente em praticamente todas as linguagens que implementam o paradigma, mesmo que de forma implícita (se tem suporta a herança, implícitamente se admite o suporte a polimorfismo também).

Ele está super alinhado com a ideia de troca de mensagens também, visto que é um benefício natual de seu uso. Fora que ele está presente na definição de Alan Kay (embora ele diga que prefere o termo genericidade) afinal late binding é o nome do processo presente em linguagens
de programação que permite a elas não relacionarem uma chamada a uma função com um código em específico, mas sim executarem baseado no contexto (que no caso da OO é o objeto que recebe a mensagem), sendo essa exatamente a definição de polimorfismo.

Em termos de percepção pública, seria o conceito mais importante dos 5 listados, sendo definido até mesmo como a essência da OO pelo Uncle Bob, além disso mesmo em situações onde não se planeja programar exatamente em orientação a objetos, se considera esse princípio como um bloco fundamental para a construção de algumas ideias como a arquitetura hexagonal ou a arquitetura limpa.

Entretanto, o conceito não é único da OO, sendo um conceito mais geral presente ao longo de vários paradigmas, muitas vezes com implementações de tipos diferentes na mesma linguagem. Apesar disso, pode-se argumentar que específicamente o polimorfismo de subtipo é único da OO, pois é um tipo dependente da capacidade da linguagem de realizar herança.

Encapsulamento

Se você leu o nosso artigo sobre o assunto (sim, gostamos de dar pitaco sobre definições por aqui), deve saber que dizer "Retenção local dos dados, junto com proteção e ocultamento do estado" é basicamente a definição completa de encapsualmento, então o lado do alinhamento conceitual, assim como os anteriores, está 100% neste princípio aqui.

Apesar de algumas linguagens não citarem o encapsulamento como princípio, o conceito está presente nelas mesmo que pela metade, o fato de ter objetos (ou como no caso de Java e Self enfatisar o conceito de campos nos objetos) mostra que elas possuem um mecanismo para menter os dados apenas sendo operados num contexto local junto as suas funções (o próprio objeto em si), por outro lado linguagens como o C++ e Eiffel apresentam mecanismos para proteção e ocultamento do estado na forma de modificadores de acesso (C++) ou asserções, pré-condições, pós-condições, e invariantes (Eiffel). Em Java mesmo temos um dos artigos mais famosos sobre a orientação a objetos que discute exatamente a aplicação do encapsulamento: Why getters and setters are evil.

Então eu diria que é um princípio que amadureceu muito bem no teste do tempo, apesar de ainda poder sofrer das mesmas críticas do polimorfismo já que não é um conceito associado exclusivamente com a OO, uma vez que é possível realizar ele com módulos (o que seria semelhante a ter um objeto singleton), ou closures, visto que elas podem atuar como objetos de pobre, entretanto, assim como no caso do polimorfismo, o conceito é aplicado "com um sabor único" na OO, visto que o mecanismo de retenção local dos dados é o objeto, e o ocultamento das informações ocorre por meio dos modificadores de acesso, essa sim uma funcionalidade amplamente associada com o paradigma.

Abstração

Ela não aparece tanto quanto o resto dos 4 pílares, entretanto, similar ao encapsulamento, sua presença ainda pode ser sentida de forma implícita ao invés de explícita, visto que, com exceção de Self, todas as linguagens mencionadas dispõe de um mecanismo de abstração de dados na forma das classes.

Adentrando na questão do Self, ele dá grande enfase nos objetos em si e na troca de mensagens, o que nos podemos aproveitar para analisar a questão do alinhamento conceitual, que nesse caso, eu diria que programar com troca de mensagens, em palavras mais modernas (apesar dos conceitos não serem exatamente a mesma coisa) seria o mesmo que "programar para uma interface", ou seja, programar apenas com abstrações, sem se preocupar com como a implementação final vai ser de fato, método esse muito bem descrito no livro Smalltalk by Example: The Developer's Guide, de Alec Sharp, como sendo "a forma orientada a objetos de se programar".

A ideia de abstração em conjunto com o polimorfismo que permite que toda metáfora de troca de mensagens funcione, pois a ideia é que não tem como saber qual vai ser o resultado da execução do código apenas olhando para as mensagens, afinal elas são abstrações (no mesmo sentido de que não dá para saber como as coisas vão executar ao ler os métodos de uma interface na OO moderna), e o resultado depende de suas implementações concretas que se encontram nos objetos em si, logo, a execução pode variar dependendo de qual objeto responde à aquela mensagem.

De todos os princípios, eu diria que a abstração é o mais fraco no critério de exclusividade, visto que abstração de dados é um princípio geral, acima do conceito de paradigmas como afirma Peter Van Roy no seu artigo Programming Paradigms for Dummies, apesar disso, novamente, estamos numa situação semelhante aos outros princípios no qual ela se utiliza de um mecanismo extremamente característico na forma das classes, que são amplamente reconhecidas como uma funcionalidade de OO, tão reconhecidas que muitas pessoas pensam que o paradigma se resume a programar com classes (esquecendo até dos objetos, e pior ainda, das mensagens no processo).

Herança

Ela está aqui pelo motivo oposto a troca de mensagens, se a troca de mensagens tem baixa pontuação de percepção pública, mas a mais altíssima em alinhamento conceitual, a herança tem o menor alinhamento conceitual dos conceitos selecionados (o que pode ser confirmado com a citação de que Alan Kay apenas adicionou herança em Smalltalk pois não sabia muito bem para quê ia servir direito, mas pensou que talvez fosse ser útil), mas possui a maior percepção pública, além de altíssimas contribuições históricas.

Para começar que ela era uma das princípais funcionalidades de Simula, era tida como a essência da OO, na era pós Smalltalk, embora isso tenha sido completamente invertido após a públicação da ideia de composição ao invés de herança pelo GoF.

Apesar disso, é o único conceito exclusivamente associado com a OO, sendo uma funcionalidade cujo a presença já seria o suficiente para diferenciar uma linguagem como orientada a objetos em muitos casos. Sendo os únicos argumentos que poderiam ir contra isso a ideia de records de Hoare, mas ela foi o que deu origem a herança em Simula, e product types, mas esse é um assunto bem diferente de herança, e nem se sofre dos mesmos problemas e polêmicas.

Conclusão

Finalmente temos ambos a perspectiva e os princípios, por isso nossa definição final fica:

Um programa orientado a objetos é visto como uma rede de objetos, sendo estes representações de conceitos do domínio, se comunicando por meio de troca de mensagens, no qual se busca programar evitando os dados, focando nas interações, de forma que uma mensagem possa carregar vários significados. Cujo os princípios são a troca de mensagens, polimorfismo, encapsulamento, abstração, e a herança. Tendo como estrutura geral algo semelhante a uma simulação do comportamento de uma parte real ou imaginária do mundo.

Enfim, essa é a minha resposta final para a pergunta apresentada no título do artigo, essa pesquisa toda deu bastante trabalho, então espero que o texto possa pelo menos ter te ensinado algo novo, ou levando algumas reflexões.

Caso concorde, ou discorde da minha definição não se esqueça de compartilhar sua opinião nos comentário, e até a próxima!

Links que podem te interessar

Ass: Suporte cansado

. . . . . . . . . . . . . . . . . . . . . . .