Orientação a objetos

Programação Orientada a Objeto (também conhecida pela sua sigla POO) ou Modelagem Orientada ao Objeto, é um modelo/paradigma de projeto e programação de software baseado na abstração digital do mundo real, através da composição e interação entre diversas unidades chamadas de 'objetos' e as classes (representando objetos reais contendo identidade, propriedades e, métodos); baseado em quatro principais componentes da programação: abstração digital, encapsulamento, herança e, polimorfismo.
Dentre as várias abordagens da POO, as baseadas em classes são as mais comuns: usando um objeto que pode ser manipulado, criado a partir de uma classe através do instanciamento. Estes possuem métodos que modificam seus próprios dados, definindo o tipo do objeto. A classe determina o comportamento (métodos), estados possíveis (atributos) e, o relacionamento com os outros objetos. A maior alternativa as classes é o uso de protótipos, onde objetos são cópias de outros, onde apenas a herança simples é implementada pela cópia.
Em alguns contextos, o termo modelagem orientada ao objeto (MOO) é preferível ao termo POO. De fato, o paradigma "orientado ao objeto" tem origem nos estudos da cognição e influenciou a inteligência artificial e a linguística, dada a relevância para a abstração de conceitos do mundo real. A MOO é considerada a melhor estratégia para diminuir o "gap semântico" (o hiato entre o mundo real e a representação digital dele), e facilita a comunicação das partes interessadas no modelo ou software (e.g. o modelador e o usuário final) na medida em que conceitos, terminologia, símbolos, grafismo e estratégias, são, potencialmente, mais óbvios, intuitivos, naturais e exatos.
Dentre as várias abordagens de orientação ao objeto, as baseadas no uso de classes são as mais comuns: esta representa um conjunto de objetos com características semelhantes; define o comportamento dos objetos através de métodos; define quais estados ele é capaz de manter através de seus atributos. Esta pode gerar subclasses que herda as todas as características.
Um objeto é algo que pode ser manipulado, é criado a partir de uma classe através do processo de programação chamado instanciamento (instanciar um objeto). Onde os objetos são operados com o conceito de 'this' (isto) ou 'self' (si), de forma que seus métodos (muitas vezes) modifiquem os dados da própria instância, que também define o tipo do objeto. Cada classe determina o comportamento (definido nos métodos) e estados possíveis (atributos) de seus objetos, assim como o relacionamento com outros objetos.
A alternativa mais usual ao uso de classes é o uso de protótipos. Neste caso, os objetos são cópias de outros objetos, e não são mais instâncias de classes; as linguagens Javascript e Lua são exemplos onde o POO é baseada em protótipos. A diferença prática mais evidente é neste tipo de POO/protótipos, apenas a herança simples é implementada pela cópia do objeto. Assim, implementa-se um conjunto de classes passíveis de serem instanciadas como objetos, por exemplo na linguagem Python e C++ - ou objetos protótipos que são copiados e alterados, por exemplo nas linguagens JavaScript e VimL).
Muitas das linguagens de programação mais utilizadas atualmente (talvez a maioria) são multi-paradigma com suporte nativo à POO, como por exemplo: C++, C#, VB.NET, Java, Object Pascal, Objective-C, Python, SuperCollider, Ruby e Smalltalk. Já as linguagens ActionScript, ColdFusion, Javascript, PHP (a partir da versão 4.0), Perl (a partir da versão 5), Visual Basic (a partir da versão 4), VimL (ou Vim script) são exemplos de linguagens de programação com suporte a orientação a objetos.
Vivace é um exemplo de linguagem sem suporte à POO.
Além da programação estruturada, a POO incorpora as seguintes diretrizes:
Tipo de Dados (TD ou simplesmente 'tipo') é um conjunto de valores que possuem propriedades em comum. Tipo abstrato de dados (TAD) é um Tipo de Dados em que o foco recai sobre a operações nos valores do tipo, em contraste com a representação destes valores. Estas operações escondem e protegem a representação dos dados enquanto facilitam o seu uso/manipulação. A POO é frequentemente compreendida e descrita como alicerçada nos TADs.
Linguagens pré-POO (em especial as que comportavam a programação estruturada) forneceram o seguinte legado à POO:
Os atributos e métodos podem ser referentes a uma classe (e todas as suas instâncias) ou a uma única instância. O vínculo dos atributos aos métodos, de forma a manter uma interface bem definida para operação sobre os dados, e a evitar corrupção dos dados, é chamado de encapsulamento. O encapsulamento foi responsável pelo conceito de 'ocultamento de dados', central para a POO. O encapsulamento pode ser realizado através de convenções (em Python, underscores demarcam métodos e atributos protegidos e privados), ou via recursos da linguagem (em Java ou C++, um método privado só é acessado pela própria classe). Encapsulamento incentiva o desacoplamento.
Quando um objeto contém outros objetos, diz-se que há composição de objetos. A composição de objetos é usada para representar uma relação 'tem um', usualmente uma meronímia. Já a herança (quase sempre suportada pelas linguagens que utilizam classes) apresenta relações 'é um' (i.e. 'é um tipo de'), ou seja, relações de hiperonímia cujo resultado final é a árvore taxonômica. Na herança, tipicamente todos os atributos e métodos da classe pai/mãe estão também disponíveis na classe filha, embora seja comum que algumas características sejam substituídas. Assim, a herança permite reúso facilitado de características e muitas vezes reflete relações do mundo real de forma intuitiva. Ambas a 'composição de objetos' e a 'herança' constituem hierarquias entre as classes e objetos na POO.
Herança múltipla ocorre quando uma classe é filha de mais de uma classe. Mixin pode ser considerado um tipo especifico de herança, embora haja uma diferença crucial: a classe da qual são herdadas as características não é considerada pai/mãe.
Polimorfismo é quando alguma rotina pode ser chamada para objetos diferentes. Por exemplo, assuma que a função retornaCorPrincipal() possa ser chamada tanto em um objeto da classe Imagem quanto da classe Video. O polimorfismo é um tipo de abstração que simplifica códigos externos à hierarquia de classes e uma separação forte das responsabilidades (separation of concerns).
Há orientações diversas para a POO, muitas de reconhecida complexidade. Por exemplo, o princípio da composição sobre herança advoca que a composição de objetos é preferida à herança. O princípio aberto/fechado advoca que classes e funções deve ser 'abertas p extensão mas fechadas para modificação'.
Subtipos comportamentais fortes (ou princípio de substituição de Liskov, LSP do inglês Liskov substitution principle) são filhos que mantém todas as características dos pais. Dito de outra forma, seja 's' tal que todas as propriedades dos objetos t da classe T estão também presentes no objeto s da classe S, em que S é subtipo de T. Tal 's' é chamado de subtipo comportamental forte, subtipo forte, subtipo de Liskov, e variações semelhantes.
As críticas mais recorrentes à POO podem ser resumidas em:
Joe Armstrong: "O problema com linguagens orientadas a objetos é que você tem todo esse ambiente implícito que elas levam consigo. Você queria uma banana mas o que você conseguiu foi um gorila segurando a banana e uma selva inteira."
Alexander Stepanov: "Dizer que tudo é um objeto é simplesmente dizer nada."
Steve Yegge: "Por que alguém iria tão longe para colocar um só aspecto da fala [substantivos] em um pedestal?"
Eric Steven Raymond: "A POO encoraja programas com muitas camadas e destrói a transparência."
Rob Pike: "A POO constitui os números romanos da computação."
Da passagem de mensagens entre objetos: o cliente (um objeto) emite uma mensagem para o servidor (outro objeto) em que solicita um serviço (i.e. que uma rotina qualquer seja executada) que pode implicar a emissão/retorno de uma resposta ao cliente. Um servidor pode ser cliente de outros (sub-)servidores. Esta resposta pode ser síncrona (quando o cliente aguarda a resposta do servidor para executar qualquer outra instrução) ou assíncrona (envolvendo paralelismo de atividades).
Um exemplo simples: um instituto (cliente), solicita um buffet para uma empresa especializada (servidor). Esta empresa (agora cliente) solicita salgados e sucos de outras empresas (sub-servidores). O instituto pode ficar paralisado enquanto não chega o buffet (envio síncrono de mensagem) ou continuar suas atividades (envio assíncrono de mensagem). Neste último caso, o instituto deve ser capaz de lidar com a interrupção das atividades (ou parte delas) assim que chegar o buffet.
A existência/espera de uma resposta pode ser inerente ao modelo de envio da mensagem ou ser realizada através do disparo de uma nova mensagem pelo servidor.
A um servidor e seus métodos são atribuídas responsabilidades específicas. No exemplo anterior, a empresa de buffet não deve entregar um brinquedo, e a comida deve ser apropriada (não apenas várias barras de chocolate, por exemplo).
Uma responsabilidade que todos os servidores tem é a de manter seus dados consistentes. Idealmente, não deve ser possível tornar inconsistentes os dados de um objeto. Um objeto tem a responsabilidade de manter uma boa interface para se comunicar com outros objetos.
Outra responsabilidade genérica e usual é a de que as operações tenham um sentido razoável. Por exemplo, se um cliente envia uma mensagem que satisfaz alguma pré-condição, é obrigação do método ativado retornar um resultado que reflita a pós-condição.
A modularidade de um programa é uma medida que reflete o quanto ele é composto por partes separadas (chamadas de módulos). Na POO, a modularidade é preferencialmente (ou basicamente) centrada das TADs, o que pode ser chamado de modularidade centrada nos dados e possui as seguintes características:
Enquanto na PE há bibliotecas de rotinas (procedure libraries), na POO há bibliotecas de classes, que podem (até certo ponto, em geral, etc) serem compreendidas com conjuntos de TADs reutilizáveis. Desafios para a reusabilidade:
Nota: a modificação deve ser usada com cuidado. A modificação direta da rotina ou objeto implica dificuldades quando alguma versão nova da biblioteca é utilizada. Preferencialmente, as modificações devem ser feitas de forma modular, i.e. separando as contribuições locais das originais. Na POO, a herança ameniza este problema. Veja também o aberto/fechado.
As ações devem sempre ser realizadas através de algum objeto.
Metáforas são recorrentes em computação: mensagens, inteiros, etc. Classes e objetos são, em parte, inspirados na teoria de conceitos e fenômenos:
Muitas classes representam conceitos da vida real pois muitos programas administram coisas do mundo real. No entanto, é comum que se faça uso também de conceitos imaginados (e.g. uma tabela hash). Ambos estes fatos favorecem a utilização da lógica fuzzy, mas na prática conceitos aristotélicos são convenientes para a construção de classes.
O estabelecimento de conceitos a partir de conjuntos de fenômenos é chamado de classificação. A intenção sendo as propriedades definidoras que são compartilhadas dentre os fenômenos do conceito. A exemplificação é o oposto da classificação: um subconjunto da extensão do conceito.
Agregar é formar um conceito que possui um número de partes (outros conceitos), i.e. é formar uma holonímia. Decompor é partir um conceito em um número de partes (outros conceitos), é formar meronímias. A intenção de um conceito agregado pode ser a intersecção das intenções de suas partes, pode ser a soma de suas intenções, ou pode possuir propriedades adicionais (em que o todo é mais que a soma das partes). Este último caso é útil na escrita de sistemas que envolvem sistemas complexos, e.g. para otimização bioinspiradas como por colonia de formigas, ou por enxame de partículas.
Generalização é formar um conceito mais amplo a partir de um (ou mais) conceitos mais estritos, é formar um hiperônimo. Especialização é formar um conceito mais estrito a partir de um conceito mais geral, é formar um hipônimo. A extensão de uma especialização é um subconjunto de sua generalização. A intenção de uma especialização (aristotélica) inclui a intenção de sua generalização. A especialização de um conceito é naturalmente expressa como herança de uma classe por outra, ou pela instanciação de uma classe.
Na POO, é/são documentado(s) o(s):
A documentação pode ser escrita antes, durante ou depois da programação (e tipicamente, para um mesmo software, é escrita durante estes três momentos). A documentação pode ser distinta do programa, mas pode estar dentro do programa ou o programa dentro dela.
Os seguintes verbetes foram contextualizados no restante deste artigo: