Imagem de OOP: no meio OOP, e ao redor do círculo 4 círculos com herança, abstração, polimorfismo e encapsulamento.
Figura 01 — OOP

Programação Orientada a Objetos

Mayara Rysia
11 min readOct 1, 2020

Os quatro princípios da POO são herança, encapsulamento, abstração e polimorfismo. Nesse artigo você vai aprender sobre esses quatro pilares, as vantagens e as desvantagens de POO. Leia até o final e deixe seu comentário =)

A Programação Orientada a Objetos (POO ou OOP, do inglês), é um paradigma de desenvolvimento de software ainda muito utilizado atualmente, é suportado por diversas linguagens de programação, a citar, C#, C++, VB.NET, Objective-C, Swift, PHP, Python, Ruby, Javascript, Java, e contrapõe o paradigma da programação procedural ou estruturada (procedural programming). Por meio da OOP nós podemos tornar o código organizado, reusável e lógico, mais seguro e de fácil manutenção. Ele segue o princípio DRY, o que torna os programas muito mais eficientes.

O OOP foi empregado por Alan Kay na década de 60 durante a pós-graduação. A inspiração veio do Sketchpad de Ivan Sutherland pois representavam objetos como estruturas de dados com imagens gráficas exibidas em uma tela de osciloscópio.

A primeira linguagem de programação reconhecida como “Orientada a Objetos” foi a Simula, pois introduziu uma série de conceitos que formaram a base de OOP como a introdução de classes, herança e métodos virtuais. Alan Kay, com base nessa linguagem, foi co-criador da linguagem Smalltalk.

A grande ideia dele era usar minicomputadores encapsulados em software que se comunicava por meio de passagem de mensagens em vez de compartilhamento direto de dados.

I made up the term ‘object-oriented’, and I can tell you I didn’t have C++ in mind.” (Alan Kay, OOPSLA ‘97)

De maneira básica, o Alan Kay resumiu o OOP em 3 ingredientes essenciais: message passing, encapsulation e dynamic binding, onde a combinação da message passing e encapsulation servem para os seguintes propósitos:

  • encapsular o estado por meio do isolamento de outros objetos e o controle das mudanças do estado somente será feito localmente;
  • desacoplar os objetos uns dos outros;
  • adaptabilidade do tempo de execução.

A ideia dele é que os objetos se comuniquem por passagem de mensagem. Suas ideias foram inspiradas em células biológicas e em estruturas algébricas.

Se você quiser conhecer mais sobre essa história, recomendo o artigo do Eric Elliot: The Forgotten History of OOP.

Nos dias atuais, o conceito da Programação Orientada a Objetos, representa elementos do mundo real abstraídos computacionalmente em forma de objetos, ou seja, um objeto é tudo aquilo que existe. Ele pode ser um animal, pessoa, carro, relógio e etc sempre a depender do contexto em questão.

Esses objetos são dados abstratos que possuem uma representação interna por meio de atributos de dados, que são as características daquele objeto, e uma interface para interação com objetos por meio dos métodos, que são os comportamentos do objeto com a implementação “escondida” e isso significa que poderemos executar as ações do objeto sem precisar entender como elas funcionam. Para facilitar a criação da representação do objeto com atributos e métodos pergunte-se: quais abstrações de dados compõem o meu objeto? E lembre-se sempre: nenhum objeto pode acessar os atributos de outros diretamente, apenas por meio dos métodos. Em suma, cada objeto é definido com suas próprias propriedades.

Vejamos alguns exemplos:

Desenho que representa uma pessoa, com atributos e comportamentos.
Figura 2 — Objeto Person

Uma Person (pessoa) tem as seguintes características: nome, data de nascimento, nacionalidade e cor. E ela faz as seguintes ações: ver, comer, andar, dormir, ouvir e falar.

Desenho que representa uma Coordenada, com atributos e comportamentos da classe.
Figura 3 — Objeto Coordinate

O exemplo foi retirado da aula Object Oriented Programming do MIT 6.0001 Introduction to Computer Science and Programming in Python, Fall 2016.

O ponto das coordenadas do plano é composto pela abscissa e ordenada portanto: x e y. Por meio desse objeto é possível revelar o valor de x e de y bem como realizar as atualizações deles e calcular a distância entre dois pontos do plano cartesiano.

Na notação visual Linguagem de Modelagem Unificada (UML) nós representaremos os objetos da seguinte maneira:

Design da classe Person e Coordinate no diagrama UML.
Figura 4-Classe Person e Coordinate

OBS.: getters e setters são uma forma de obter e alterar os dados das classes (respectivamente), nem sempre são necessárias isso dependerá muito do contexto em questão.

Quando pensamos em criar um objeto abstraído da realidade nós estamos pensando também em criar nosso próprio dado, ou seja, nosso próprio tipo de dado. Em OOP nós modelamos os objetos usando o termo classe (class) e é por meio dela que nós podemos criar e usar nosso próprio tipo de dado.

Por exemplo, criei um tipo de dado Person que representa uma pessoa do mundo real. Considero que todo ser humano tem nome e data de nascimento (atributos) e realiza ações de andar, falar e comer (comportamentos):

Exemplo simples de uma classe pessoa

Vamos mapear mentalmente o conceito de tipo de dado com mais um exemplo: o objeto Coordenada feito na sintaxe Java.:

Classe Coordinate

Nesse exemplo, o construtor da classe define como se deve criar a instância dela , e sempre quando for criado o objeto do tipo Coordinate pela primeira vez, é necessário determinar x e y.

Logo, dados e procedimentos pertencem a classe.:

Atributos:

São os dados da classe. Vamos pensar neles como outros objetos que a compõe, como o objeto “name” do tipo de dado String que é um dado da classe Person, ou, do Objeto Person. E os dados x e y que são objetos do tipo de dado Double que pertencem a classe Coordinate.

Métodos:

São os procedimentos/comportamentos dos atributos que define como nós vamos interagir com o objeto. Vamos pensar neles como funções que trabalham na classe criada, como por exemplo, o comportamento de walk() ou talk() da pessoa, pertencem a classe Person.

Os procedimentos de alterar ou obter os valores das coordenadas foram definidos na classe Coordinate e somente assim é que podemos interagir com objetos desse tipo, portanto, se quisermos obter o valor da abscissa chamamos o método getX(), se quisermos mudar o ponto dela chamamos setX( novoValor ).

Por meio da classe Person, nós podemos criar diversos objetos que representam um ser humano, para isso é necessário criar a instância dessa classe conforme o exemplo:

Exemplo de instância da classe Person
Imagem que demonstra o resultado da manipulação de objetos do tipo Person
Figura 5 — Resultado da manipulação de objetos do tipo Person.

Por meio da classe Coordinate , nós podemos criar diversos objetos que representam pontos do plano cartesiano, e para isso é necessário criar a instância dessa classe e, para acessar os métodos, é obrigatório o uso do ponto (.) seguido do nome do método, pois, somente dessa forma, mantemos a interação com o objeto do tipo Coordinate. Vejamos o exemplo:

Exemplo de criação dos objetos Coordinate e chamada de seus métodos.
Imagem que demonstra o resultado da manipulação de objetos do tipo Coordinate.
Figura 6— Resultado da manipulação de objetos do tipo Coordinate.

Só para não haver mais dúvida alguma quanto ao tipo de dado, vamos verificar em uma condicional o tipo de dado do objeto pointA na sintaxe Java:

Condicional que verifica se o objeto pointA é do tipo Coordinate.

Essa condicional é verdadeira e será impresso na tela o texto “tipo Coordinate”.

Imagem que exibe o resultado da manipulação de objetos do tipo Coordinate
Figura 7— Resultado da manipulação de objetos do tipo Coordinate.

O OOP possui 4 princípios muito importantes:

  1. Abstração
  2. Encapsulamento
  3. Herança
  4. Polimorfismo

1. ABSTRAÇÃO

A abstração é uma das principais maneiras pela qual o ser humano lida com a complexidade. Ela divide um conceito em uma descrição simplificada ignorando detalhes sem importância e enfatizando os fundamentos necessários para aquele conceito, dentro de algum contexto.

A construção de um programa incluem elementos como funções, classes, enumerações e métodos. Na orientação a objetos, a abstração está ligada diretamente à noção de classe. Quando a abstração é usada para determinar os detalhes essenciais para algum conceito/problema, esses detalhes podem ser definidos em uma classe. É cabível a pessoa desenvolvedora escolher a abstração mais adequada ao contexto do desenvolvimento de software e ela deve ser compreendida antes de ser criada. Por exemplo, se queremos criar um aplicativo de exercícios de basquetebol, as características fundamentais de uma pessoa estão ligadas ao contexto de um atleta de basquetebol. Nesse sentido, portanto, as características fundamentais de uma abstração podem ser compreendidas de duas maneiras: por meio de atributos básicos e por meio de comportamentos ou responsabilidades básicas.

Logo, a abstração ajuda a obter o design das classes mais sucintas, focadas e compreensíveis a outra pessoa que as veem. Nela, é imprescindível considerar o que é relevante porque as abstrações dependem fortemente do contexto ou da perspectiva em questão.

2. ENCAPSULAMENTO

É uma maneira eficiente de manter os dados do objeto seguro porque a manipulação dos dados só deve ser feita por meio de procedimentos (métodos). Isso evita que haja uma alteração direta no dado da classe. Dessa maneira, o objeto vai expor apenas as informações que foram definidas através dos métodos. O encapsulamento, permite que objetos distintos criados a partir de uma classe específica tenham seus próprios valores de dados para os atributos e exibam os comportamentos resultantes. Isso torna a programação mais fácil porque os dados e o código que os manipulam estão localizados no mesmo lugar.

As 3 capacidades do encapsulamento são:

  • agrupar valores dos atributos (ou dados) e comportamentos (ou funções) que manipulam esses valores, em um objeto autocontido;
  • expor dados e funções desse objeto, que pode ser acessado por outros objetos (pode ser por meio de uma interface);
  • restringir o acesso a certos dados e funções dentro do objeto.

No exemplo da classe Coordinate , somente é possível alterar um valor da coordenada por meio do método setY(6) ou setX(9) e não diretamente com Coordinate.y = Null. Além disso, o encapsulamento assegura que quem estiver utilizando esse método não precisa saber como foi feita a implementação, pois apenas o chama e ele realiza a ação.

Portanto, esse princípio mantém o software modular e fácil de trabalhar e as classes são fáceis de gerenciar, porque seu comportamento interno não é relevante a outras classes e elas podem interagir juntas.

3. HERANÇA

A herança nos permite reutilizar informações de uma classe principal. Uma classe herda informações de outra classe, que são os atributos ou comportamentos, e por conta disso ela será filha da classe mãe. Em outras palavras, a classe que recebeu herança é chamada de subclasse, ela herda os dados e os comportamentos da classe mãe (superclasse). Vejamos um exemplo:

Representamos uma pessoa com a classe Person e agora nós vamos representar um objeto funcionário. Nós sabemos que todo funcionário é uma pessoa, um ser humano, então ele herda todas as características de um ser humano e seus comportamentos. Mas embora o funcionário herde os dados da classe Person, ele possui seus próprios dados dentro da empresa em que atua:

Imagem que desmonstra a representação de um Objeto Employee
Figura 8— Representação de um Objeto Employee

Nós percebemos características da representação do objeto Person e mais duas que pertencem apenas a Employee: identificação ( que seria uma matrícula) e departamento, que nesse exemplo, a funcionária Maria está no departamento de TI, além disso, a funcionária possui a ação de trabalhar.

Na notação visual Linguagem de Modelagem Unificada (UML) nós representaremos os objetos em classes da seguinte maneira:

Design no diagrama UML da Classe Person e Employee
Figura 9 — Classe Person e Employee

Na linguagem java utilizamos o extends para indicar herança:

Exemplo de indicação de herança no java (herança simples)

A ideia de classe abstrata toma uma formatação diferente e mais específica: as classes são criadas usando abstract class e não podem ser instanciadas porque somente serão progenitoras. A ideia é representar um objeto de maneira abstrata onde obrigatoriamente será herdado por outras classes, ou seja, a criação de atributos e métodos para o objeto pode ser realizada, mas a implementação dos métodos só poderá ser feita nas classes filhas pois ela será apenas um rascunho. Essa é a única diferença de uma classe com herança comum, ela não pode ser instanciada, todavia isso permite mais consistência ao sistema.

Exemplo de herança com classe abstrata

4. POLIMORFISMO

Polimorfismo significa várias formas. Em OOP, ocorre polimorfismo quando um objeto se comporta de várias maneiras ao receber uma informação e isso dependerá do seu tipo de criação, ou seja, nós podemos manipulá-lo de formas diferentes. Em outras palavras, o polimorfismo é quando duas classes têm a mesma descrição de um comportamento, mas as implementações desse comportamento podem ser diferentes.

Na herança, um método da superclasse pode ser reescrito na subclasse e apresentar outro comportamento, um comportamento único para essa classe filha em específico. Isso será possível através da sobreposição/substituição do método (method overriding).

Também é possível realizar polimorfismo sem herança, quando o mesmo método de uma classe executa comportamentos diferentes mas com o mesmo nome do método e isso é possível por meio da sobrecarga de método (method overloading).

Overload

É a criação de mais de um método com mesmo nome, mas com comportamentos diferentes na mesma classe. É chamado de polimorfismo em tempo de compilação ou polimorfismo de sobrecarga e não acontece herança. Nesse polimorfismo nós teremos assinaturas diferentes na mesma classe. Vejamos o exemplo na sintaxe Java:

OBS.: Todo método possui assinatura, ela consiste na quantidade e nos tipos dos parâmetros.

Exemplo de classe com sobrecarga de método — Métodos com mesmo nome e assinaturas diferentes.
A figura demonstra a execução de um exemplo de sobrecarga
Figura 10 — Resultado do exemplo com a classe com sobrecarga de método.

Override

O overriding acontece nas subclasses. É chamado de polimorfismo em tempo de execução ou polimorfismo de sobreposição, onde o método possui a mesma assinatura em classes diferentes. Vejamos o exemplo na sintaxe Java:

Exemplo de classe com sobreposição de método — Métodos com mesmo nome e assinaturas iguais.
Imagem que exibe o resultado do exemplo com a classe com sobreposição de método
Figura 11 — Resultado do exemplo com a classe com sobreposição de método.

A classe mãe é Shape e ela contém o método draw que nas classes filhas, draw foi reescrito assumindo formas diferentes de respostas.

As interfaces também são um meio pelo qual você pode obter polimorfismo. Um exemplo disso pode ser como os animais “falam”. Um gato pode miar, mas um cachorro late. Ambos os animais podem “falar”, mas a implementação do comportamento é diferente. Exemplo:

Polimorfismo com interface

Exemplo do diagrama UML:

Interação interface e classe no diagrama
Figura 12 — Interação interface e classe no diagrama

VANTAGENS

O paradigma de OOP permite a leitura fácil do código, a organização, a manutenção, a reutilização e a segurança dos dados por meio de encapsulamento e herança. Além disso, o código torna-se muito mais fácil de usar uma vez que tudo é reflexo de entidades do mundo real.

DESVANTAGENS

O código pode se tornar muito confuso se a interpretação dos objetos ou dos módulos não estiverem corretas e ocasionará em redundância ou ineficiência e consequentemente em erros inerentes. Em outras palavras, se a abstração de determinado contexto estiver errada muito provavelmente ocasionará em uma série de falhas desde o desenvolvimento até a entrega final no que se refere a manutenabilidade e a sustentabilidade do sistema, caso não for corrigido.

Por isso, esse paradigma requer mais planejamento, portanto, realizar um design claro das modularizações com antecedência é a receita para evitar desastres. Logo, programas em OOP normalmente exigem um esforço computacional maior do que programas na linguagem funcional.

Se esse conteúdo fez ou não fez sentido para você deixe seu comentário com sugestões e observações.

Obrigada : )

--

--