Ir para o conteúdo

Programação orientada a objetos

Origem: Wikipédia, a enciclopédia livre.
(Redirecionado de Encapsulamento)

Programação orientada a objetos (POO; em inglês, object-oriented programming, OOP) é um paradigma de programação que organiza sistemas de software em torno de objetos que combinam dados e operações associadas a esses dados. Em muitas linguagens, os objetos têm estado, comportamento e identidade; os programas são construídos pela criação de objetos e pela interação entre eles.[1][2]

Representação esquemática de um objeto, com atributos e métodos.

A POO é frequentemente implementada por meio de classes e instâncias, mas também pode ser baseada em protótipos. Conceitos comuns incluem abstração, encapsulamento, composição, herança e polimorfismo. Nem todas as linguagens orientadas a objetos implementam todos esses mecanismos da mesma maneira, e não há uma lista única de características que seja universalmente aceita para definir o paradigma.[1][3]

Muitas linguagens de programação são multiparadigma: permitem combinar a POO com programação imperativa, procedural, funcional ou outros estilos. Exemplos de linguagens com suporte a objetos, em graus e modelos diferentes, incluem Java, C++, C#, Python, Lua, PHP, Ruby, Perl, Object Pascal, Objective-C, Dart, Swift, Scala, Common Lisp e Smalltalk. Java e C++ são exemplos de linguagens baseadas em classes; Smalltalk é historicamente associado ao paradigma, enquanto JavaScript implementa herança por protótipos e também possui sintaxe de classes. As características concretas variam de linguagem para linguagem.[2][4][5]

Conceitos fundamentais

[editar | editar código]

A POO agrupa dados e operações em unidades com responsabilidades definidas. Um objeto pode representar uma entidade do domínio de um programa, como uma conta bancária, ou algo abstrato da própria computação, como um fluxo de entrada, uma conexão de rede ou um serviço. Não é necessário que todo objeto represente uma coisa física do mundo real.[6][2]

Objetos, estado, identidade e comportamento

[editar | editar código]

Um objeto é uma unidade de execução ou modelagem que reúne dados e comportamento segundo as regras da linguagem e do sistema em que é utilizado. Os atributos ou campos armazenam dados associados ao objeto; os métodos são operações que podem consultar esses dados ou produzir efeitos. Os termos exatos e as possibilidades de acesso dependem da linguagem.

O estado corresponde aos valores relevantes mantidos pelo objeto num dado momento. A identidade permite distinguir uma instância de outra, mesmo quando ambas possuem atributos iguais. O comportamento é a maneira pela qual o objeto responde às operações que lhe são disponibilizadas. Algumas linguagens e estilos de programação permitem objetos imutáveis, cujo estado não é alterado após a criação; portanto, a mutação não é requisito universal da POO.[2][6]

Classes e instâncias

[editar | editar código]
Exemplo de diagrama de classes: classes podem descrever atributos, operações e relações de herança.

Numa abordagem baseada em classes, uma classe descreve um conjunto de objetos com estrutura e comportamento comuns. Ela pode declarar atributos, métodos, regras de acesso e mecanismos para inicializar as instâncias. Um objeto criado a partir de uma classe é denominado instância dessa classe. A classe funciona como uma descrição do tipo de objeto; não é, em geral, a mesma coisa que cada objeto concreto criado a partir dela.[2][4]

Uma classe pode definir métodos de instância, relacionados a objetos individuais, e métodos ou atributos estáticos, associados à própria classe, conforme o modelo da linguagem. Atributos de instância pertencem a cada objeto concreto; atributos estáticos são associados à classe e podem ser compartilhados entre suas instâncias. Um construtor é um mecanismo de inicialização utilizado na criação de instâncias em linguagens que oferecem esse recurso; sua sintaxe e semântica não são iguais em todas as linguagens.

Abstração

[editar | editar código]

A abstração consiste em representar as propriedades relevantes de um sistema para determinado propósito, deixando de lado detalhes que não são necessários naquele nível de descrição. Numa classe `ContaBancaria`, por exemplo, a interface pode disponibilizar operações como depositar, sacar e consultar saldo sem expor como os dados são armazenados internamente. A abstração não exige que o programa reproduza literalmente objetos do mundo real; também pode representar conceitos de software, processos ou serviços.[6][3]

Encapsulamento e ocultação de informação

[editar | editar código]

O encapsulamento reúne dados e operações numa unidade e define como outras partes do programa podem interagir com ela. A ocultação de informação é uma prática relacionada, mas distinta: procura impedir que detalhes internos sejam acessados ou dependidos diretamente por código externo. Linguagens como Java oferecem modificadores de acesso, como `private`, `protected` e `public`; outros sistemas usam mecanismos diferentes, e nem todas as linguagens impõem as mesmas restrições.[4][3]

Encapsular não significa que os objetos estejam isolados de toda influência externa, nem garante por si só que as operações de um objeto não afetem outros. Métodos podem modificar estado compartilhado, chamar outros objetos ou interagir com sistemas externos. O objetivo é definir limites de acesso e contratos de interação que reduzam dependências desnecessárias.

Associação, agregação e composição

[editar | editar código]

Uma associação representa uma relação entre objetos ou classes. Por exemplo, um objeto `Pedido` pode estar associado a um objeto `Cliente`. A associação pode ter multiplicidade — um para um, um para muitos ou muitos para muitos — e pode ser navegável numa ou em ambas as direções, conforme o modelo.

A agregação e a composição são formas específicas de representar relações entre um todo e suas partes, especialmente em modelagem com a linguagem de modelagem unificada (UML). A composição costuma expressar uma relação de propriedade ou dependência mais forte entre o todo e as partes; a agregação representa uma relação mais fraca. O significado exato de ciclo de vida e propriedade deve ser definido pelo modelo e pela linguagem, e não pode ser inferido apenas da palavra usada numa descrição informal.[7]

Relações entre classes e objetos

[editar | editar código]

Herança e delegação

[editar | editar código]

A herança permite definir uma classe a partir de outra, reutilizando ou especializando parte de sua estrutura e comportamento. A classe derivada é frequentemente chamada de subclasse, e a classe da qual deriva, de superclasse. A subclasse pode adicionar atributos e métodos ou redefinir métodos herdados, de acordo com as regras da linguagem. A herança é usada para representar relações de especialização, mas não é obrigatória em toda abordagem orientada a objetos.[4][3]

Algumas linguagens permitem herança de implementação a partir de uma única classe; outras permitem herança múltipla de classes. A herança múltipla pode introduzir questões adicionais, como a resolução de nomes, conflitos entre implementações e a presença de mais de um caminho até uma classe ancestral. Mecanismos como interfaces, traços (traits) e mixins oferecem outras formas de especificar ou combinar comportamentos.

A delegação ocorre quando um objeto encaminha uma operação a outro objeto que possui o comportamento adequado. Em sistemas baseados em protótipos, a resolução de propriedades e métodos por meio de uma cadeia de protótipos é uma forma importante de reutilização e delegação. A herança e a delegação são mecanismos relacionados, mas não idênticos.[5]

Composição

[editar | editar código]

A composição de objetos consiste em construir objetos complexos a partir de outros objetos que colaboram entre si. Por exemplo, um `Pedido` pode conter itens, enquanto cada item referencia um produto e uma quantidade. A composição favorece a combinação de comportamentos sem exigir que todas as relações entre componentes sejam expressas por herança.

Herança e composição resolvem problemas diferentes. A herança é frequentemente apropriada para especialização quando a subclasse pode ser utilizada de modo coerente nos contextos esperados para a superclasse; a composição combina componentes que mantêm responsabilidades distintas. Nenhuma das duas técnicas garante, isoladamente, uma arquitetura reutilizável ou de fácil manutenção.[6]

Polimorfismo e subtipagem

[editar | editar código]

O polimorfismo permite que uma mesma operação seja utilizada com valores ou objetos de diferentes tipos, com resultados determinados pelo tipo ou pela implementação concreta. Na POO baseada em subtipagem, diferentes classes podem implementar uma interface comum; o código que utiliza essa interface não precisa conhecer a classe específica de cada objeto.[3][4]

A subtipagem descreve uma relação entre tipos: um valor de um subtipo pode ser utilizado em contextos que aceitam o tipo mais geral, desde que respeite o contrato exigido pelo contexto. A ligação dinâmica (ou despacho dinâmico) seleciona, em tempo de execução, a implementação de um método com base no objeto concreto ou nas regras de despacho da linguagem. Polimorfismo, subtipagem e ligação dinâmica estão frequentemente relacionados, mas não são sinônimos.

A sobrecarga de métodos, na qual várias operações compartilham um nome e são selecionadas de acordo com os parâmetros, é outro mecanismo. Ela não deve ser confundida com o despacho dinâmico por subtipagem.

Interfaces e classes abstratas

[editar | editar código]

Uma interface especifica operações ou obrigações que um tipo deve disponibilizar, sem precisar definir toda a implementação. Classes diferentes podem implementar a mesma interface e ser utilizadas por código que depende apenas desse contrato. Uma classe abstrata pode fornecer parte de uma implementação compartilhada e declarar operações que suas subclasses devem completar; em geral, não pode ser instanciada diretamente. Algumas linguagens também permitem impedir que uma classe seja estendida; em Java, o modificador `final` cumpre esse papel. O suporte e as regras variam entre linguagens; algumas permitem que um tipo implemente várias interfaces independentemente da quantidade de superclasses que pode estender.[4][3]

Ligação dinâmica e passagem de mensagens

[editar | editar código]

Em linguagens como Smalltalk, a interação entre objetos é descrita em termos de envio de mensagens: um objeto solicita que outro execute uma operação. Em outras linguagens, é comum falar em chamadas de métodos. Os conceitos não são necessariamente implementados da mesma maneira: uma chamada pode resolver o método estaticamente, durante a compilação, ou dinamicamente, durante a execução.

A passagem de mensagens também não deve ser confundida com comunicação entre processos por uma rede. No contexto da POO, o termo pode descrever a interação entre objetos dentro de um mesmo programa; sistemas distribuídos podem usar mecanismos de mensagens próprios para comunicar componentes em processos ou máquinas diferentes.[8]

O exemplo seguinte, em Java, mostra uma interface `Forma` e duas classes que implementam a operação `area`. O método `areaTotal` recebe uma lista de objetos do tipo `Forma` sem precisar distinguir se cada elemento é um círculo ou um retângulo. Ao chamar `area()`, a implementação correspondente ao objeto concreto é executada, ilustrando o polimorfismo por subtipagem e despacho dinâmico.

import java.util.List;

interface Forma {
    double area();
}

final class Circulo implements Forma {
    private final double raio;

    Circulo(double raio) {
        this.raio = raio;
    }

    @Override
    public double area() {
        return Math.PI * raio * raio;
    }
}

final class Retangulo implements Forma {
    private final double largura;
    private final double altura;

    Retangulo(double largura, double altura) {
        this.largura = largura;
        this.altura = altura;
    }

    @Override
    public double area() {
        return largura * altura;
    }
}

class Exemplo {
    static double areaTotal(List<Forma> formas) {
        double total = 0.0;
        for (Forma forma : formas) {
            total += forma.area();
        }
        return total;
    }
}

No exemplo, os campos `raio`, `largura` e `altura` são privados, e as classes implementam um contrato comum. O código ilustra uma forma de encapsulamento e de polimorfismo; não significa que todas as linguagens orientadas a objetos exijam classes, interfaces ou campos privados.

Abordagens baseadas em classes e em protótipos

[editar | editar código]

Na programação baseada em classes, as classes descrevem a estrutura e o comportamento de suas instâncias, e a herança costuma ser definida entre classes. Esse modelo é usado, entre outras, por Java, C++ e C#. Algumas linguagens, como Python, combinam o modelo de classes com recursos de outros paradigmas.

Na programação baseada em protótipos, os objetos podem herdar propriedades ou delegar a resolução de operações a outros objetos que funcionam como protótipos. Em JavaScript, a cadeia de protótipos é o mecanismo subjacente à herança baseada em objetos; a sintaxe `class`, incorporada à linguagem, oferece uma forma de declarar classes sobre esse mecanismo, mas não substitui o modelo de protótipos.[5]

A distinção não significa que uma abordagem seja universalmente superior à outra. Os modelos oferecem diferentes formas de criar objetos, reutilizar comportamento e organizar relações entre componentes. A escolha depende da linguagem, do problema e das convenções do sistema.

História

[editar | editar código]
Captura de tela de um ambiente de desenvolvimento Smalltalk, linguagem historicamente associada à POO.

Os antecedentes da POO incluem linguagens e sistemas que procuraram representar entidades persistentes, estados e interações em programas de simulação. A linguagem Simula, desenvolvida por Ole-Johan Dahl e Kristen Nygaard no Centro Norueguês de Computação durante a década de 1960, introduziu mecanismos de classe e subclasse usados para organizar simulações. O instituto identifica Simula como a primeira linguagem de programação orientada a objetos.[9]

Nos anos 1970, pesquisadores da Xerox PARC, incluindo Alan Kay, Dan Ingalls e Adele Goldberg, desenvolveram e aperfeiçoaram Smalltalk, um ambiente de programação no qual objetos, classes, interação por mensagens e uma interface gráfica integrada desempenhavam papéis centrais. Kay descreveu posteriormente a evolução das ideias e experiências que levaram ao sistema Smalltalk, situando-as num contexto mais amplo de computação interativa, gráficos e trabalho experimental com linguagens.[8]

Durante as décadas de 1980 e 1990, mecanismos orientados a objetos foram incorporados a linguagens de uso mais amplo. C++ evoluiu a partir de uma extensão de C denominada C with Classes, cujo desenvolvimento começou em 1979. Java, desenvolvida inicialmente no início da década de 1990, foi apresentada publicamente em 1995. Outras linguagens passaram a oferecer recursos orientados a objetos em combinação com outros paradigmas; a difusão e a implementação desses recursos variaram de acordo com a linguagem e o ambiente de desenvolvimento.[10][11]

Aplicações e projeto de software

[editar | editar código]

A POO é utilizada em sistemas de negócio, interfaces gráficas, simulação, jogos, ferramentas de desenvolvimento e bibliotecas de software, entre outras áreas. Ela pode facilitar a organização de sistemas nos quais componentes mantêm estado e colaboram por meio de interfaces explícitas. A adequação depende, contudo, da natureza do problema, das exigências de desempenho e concorrência e da forma como responsabilidades e dependências são definidas.[6][2]

O projeto orientado a objetos é a atividade de identificar responsabilidades, definir contratos e estruturar relações entre as unidades de um sistema. Ele não é idêntico à POO: a análise e o projeto podem recorrer a modelos de objetos antes da implementação, e um programa pode empregar objetos sem seguir uma metodologia específica de projeto orientado a objetos.

A linguagem de modelagem unificada (UML) é uma linguagem de modelagem padronizada que inclui diagramas de classes, objetos, sequência, estados e atividades, entre outros. Diagramas de classes são frequentemente usados para representar tipos, atributos, operações e relações, mas UML não é uma linguagem de programação nem um requisito para desenvolver software orientado a objetos.[7]

Vantagens e limitações

[editar | editar código]

A POO pode tornar explícitas as responsabilidades dos componentes, organizar o acesso ao estado e permitir que implementações distintas sejam utilizadas por meio de contratos comuns. Encapsulamento e interfaces bem definidas podem ajudar a limitar o impacto de alterações internas; composição e polimorfismo podem permitir a combinação de componentes sem que o código cliente dependa de cada implementação concreta.[6][3]

Esses benefícios não são automáticos. Hierarquias de herança profundas, dependências excessivas entre classes, estado compartilhado difícil de controlar ou abstrações que não correspondem às necessidades do sistema podem tornar o software complexo de compreender e modificar. O uso de classes e objetos tampouco garante reutilização, segurança, desempenho ou qualidade por si só. Em alguns problemas, abordagens procedurais, funcionais, orientadas a dados ou combinações de paradigmas podem ser mais adequadas.

Ver também

[editar | editar código]

Referências

  1. 1 2 Deborah J. Armstrong, "The Quarks of Object-Oriented Development", Communications of the ACM, vol. 49, n.º 2, fevereiro de 2006, p. 123–128. DOI: 10.1145/1113034.1113040.
  2. 1 2 3 4 5 6 MDN Web Docs, "Object-oriented programming", Mozilla.
  3. 1 2 3 4 5 6 7 Benjamin C. Pierce, Types and Programming Languages, MIT Press, 2002, seção 18.1, "What is Object-Oriented Programming?", ISBN 978-0-262-16209-8.
  4. 1 2 3 4 5 6 Oracle, "Lesson 8: Object-Oriented Programming".
  5. 1 2 3 MDN Web Docs, "Inheritance and the prototype chain", Mozilla.
  6. 1 2 3 4 5 6 Grady Booch, Robert A. Maksimchuk, Michael W. Engle, Bobbi J. Young, Jim Conallen e Kelli A. Houston, Object-Oriented Analysis and Design with Applications, 3.ª ed., Addison-Wesley, 2007, ISBN 978-0-201-89551-3.
  7. 1 2 Object Management Group, OMG Unified Modeling Language (OMG UML), Version 2.5.1, dezembro de 2017.
  8. 1 2 Alan C. Kay, "The Early History of Smalltalk", ACM SIGPLAN Notices, vol. 28, n.º 3, março de 1993, p. 69–95. DOI: 10.1145/155360.155364.
  9. ↑ Norsk Regnesentral, "A short history of the Norwegian Computing Center".
  10. ↑ Bjarne Stroustrup, "Stroustrup: FAQ", seção "When was C++ invented?".
  11. ↑ Oracle, "A Brief History of Java".