Orientação a objetos – Parte II

Introdução da linguagem Java, Classes, Interfaces, Objetos(Herança, Encapsulamento e Polimorfismo)

Objetos são instâncias de classes. É através deles que (praticamente) todo o processamento ocorre em sistemas implementados com linguagens de programação orientadas a objetos. O uso racional de objetos, obedecendo aos princípios associados à sua definição conforme estabelecido no paradigma de desenvolvimento orientado a objetos, é chave para o desenvolvimento de sistemas complexos e eficientes.

Um objeto é um elemento que representa, no domínio da solução, alguma entidade (abstrata ou concreta) do domínio de interesse do problema sob análise. Objetos similares são agrupados em classes.

No paradigma de orientação a objetos, tudo pode ser potencialmente representado como um objeto. Sob o ponto de vista da programação orientada a objetos, um objeto não é muito diferente de uma variável normal.

Um programa orientado a objetos é composto por um conjunto de objetos que interagem através de “trocas de mensagens”. Na prática, essa troca de mensagem traduz-se na aplicação de métodos a objetos.

As técnicas de programação orientada a objetos recomendam que a estrutura de um objeto e a implementação de seus métodos devem ser tão privativos como possível. Normalmente, os atributos de um objeto não devem ser visíveis externamente. Da mesma forma, de um método deve ser suficiente conhecer apenas sua especificação, sem necessidade de saber detalhes de como a funcionalidade que ele executa é implementada.

Encapsulação é o princípio de projeto pelo qual cada componente de um programa deve agregar toda a informação relevante para sua manipulação como uma unidade (uma cápsula). Aliado ao conceito de ocultamento de informação, é um poderoso mecanismo da programação orientada a objetos.

Ocultamento da informação é o princípio pelo qual cada componente deve manter oculta sob sua guarda uma decisão de projeto única. Para a utilização desse componente, apenas o mínimo necessário para sua operação deve ser revelado (tornado público).

Na orientação a objetos, o uso da encapsulação e ocultamento da informação recomenda que a representação do estado de um objeto deve ser mantida oculta. Cada objeto deve ser manipulado exclusivamente através dos métodos públicos do objeto, dos quais apenas a assinatura deve ser revelada.

O conjunto de assinaturas dos métodos públicos da classe constitui sua interface operacional.

Dessa forma, detalhes internos sobre a operação do objeto não são conhecidos, permitindo que o usuário do objeto trabalhe em um nível mais alto de abstração, sem preocupação com os detalhes internos da classe. Essa facilidade permite simplificar a construção de programas com funcionalidades complexas, tais como interfaces gráficas ou aplicações distribuídas. Exemplo:

public class Consulta { public static void main(String[]args){ Pessoa pess = new Pessoa(); pess.nome = "Joao"; pess.apelido = "Joca"; pess.altura = 170.0f; pess.idade = 34.0f; pess.peso = 65.0f; } } public class Pessoa { public String nome, apelido; public Float idade, peso, altura; }

Observe como seria o acesso a estes atributos em um pseudocódigo. Veja que a palavra “pess” é utilizada para referenciar um objeto da classe Pessoa criada.

Princípios da Orientação a Objetos

Em Orientação a Objetos temos três conceitos básicos:

Para uma linguagem de programação ser considerada “Orientada a Objetos” é necessário implementar mecanismos que permitam utilizar estes três conceitos básicos.

Herança

A herança é a principal característica de distinção entre um sistema de programação orientado a objeto e outros sistemas de programação. As classes são inseridas em uma hierarquia de especializações de tal forma que uma classe mais especializada herda todas as propriedades da classe mais geral a qual é subordinada na hierarquia. A classe mais geral é denominada superclasse e a classe mais especializada subclasse.

O principal benefício da herança é a reutilização de código. A herança permite ao programador criar uma nova classe programando somente as diferenças existentes na subclasse em relação à superclasse. Isto se adéqua bem a forma como compreendemos o mundo real, no qual conseguimos identificar naturalmente estas relações.

A fim de exemplificarmos este conceito, vamos considerar que queiramos modelar os seres vivos pluricelulares existentes no planeta. Podemos então começar com a classe SerVivo.

Ela modela as características que todo ser vivo deve possuir, como a capacidade de reproduzir-se ou a necessidade de alimentar-se. Sendo assim, a classe SerVivo define atributos e métodos tais como:

Os seres vivos por sua vez classificam-se em Animais e Vegetais, os quais possuem características próprias que os distingue:

Analisando o problema em questão (o de modelar os seres vivos), nós naturalmente identificamos classes que são especializações de classes mais genéricas e o conceito de herança da orientação a objeto nos permite implementar tal situação.

Os animais e vegetais antes de tudo são seres vivos e cada subclasse herda automaticamente os atributos e métodos (respeitando as regras dos modificadores de acesso) da superclasse, neste caso, a classe SerVivo. Além disso, as subclasses podem prover atributos e métodos adicionais para representar suas próprias características. Por exemplo, a classe Animal poderia definir os seguintes métodos e atributos:

A herança na programação é obtida especificando-se qual superclasse a subclasse estende. Em Java isto é feito utilizando-se a palavra chave extends:

public class SerVivo { //Definição da classe SerVivo } public class Animal extends SerVivo { //Atributos e métodos adicionais que distinguem um Animal de um SerVivo //qualquer }

Em Java, todas as classes, tanto as existentes nas APIs como as definidas pelos programadores, automaticamente derivam de uma superclasse padrão, a classe Object.

Se uma classe não especifica explicitamente uma superclasse, como o caso da classe SerVivo, então podemos considerar que esta deriva diretamente de Object, como se ela tivesse sido definida como:

public class SerVivo extends Object { ... }

Além disso, Java permite apenas herança simples, isto é, uma classe pode estender apenas de uma única outra classe.

Resumindo, a classe SerVivo define os atributos e métodos que são comuns a qualquer tipo de ser vivo. A subclasse Animal herda estes métodos e atributos, já que Animal é um SerVivo , e tem que especificar apenas seus atributos e métodos específicos.

Encapsulamento

Diferente da abordagem estruturada, onde dados e procedimentos são definidos de forma separada no código, na programação orientada a objeto os dados e procedimentos que manipulam estes dados são definidos numa unidade única, o objeto. Isso possibilita uma melhor modularidade do código, porém, a ideia principal é poder utilizar os objetos sem ter que se conhecer sua implementação interna, que deve ficar escondida do usuário do objeto que irá interagir com este apenas através de sua interface.

“À propriedade de se implementar dados e procedimentos correlacionados em uma mesma entidade e de se proteger sua estrutura interna escondendo-a de observadores externos dá-se o nome de encapsulamento.“

O objetivo do encapsulamento é separar o usuário do objeto do programador do objeto e seus principais benefícios são:

Via de regra, as variáveis de instância declaradas em uma definição de classe, bem como os métodos que executam operações internas sobre estas variáveis, se houverem, devem ser escondidos na definição da classe. Isso é feito geralmente através de construções nas linguagens de programação conhecidas como modificadores de acesso, como por exemplo, o public, protected e private do Java. Quando definimos uma classe, é recomendado (para alguns é uma regra sagrada) que declaremos públicos apenas os métodos da sua interface. É na interface (ou protocolo) da classe que definimos quais mensagens podemos enviar às instâncias de uma classe, ou seja, quais são as operações que podemos solicitar que os objetos realizem. Por exemplo, na classe Ponto abaixo, deveríamos ter feito seus atributos protegidos e apenas o método público:

public class Ponto { private double x; private double y; public void girar (int grau, Ponto p) { //Código para girar o ponto em torno de outro ponto p a quantidade definida em //grau } //Os métodos públicos consistuem a interface da classe. public void setX (double k) { x = k; } public void setY (double k) { y = k; } public double getX () { return x; } public double getY () { return y; } }

É comum em programação orientada a objeto definir métodos gets e sets que provêm acesso aos dados protegidos da classe.

Criando Objetos e Acessando Dados Encapsulados

Criamos objetos em Java de forma muito similar a criação de variáveis de tipos primitivos. Se uma aplicação quisesse usar a classe Círculo, poderia declarar uma variável deste tipo da seguinte forma:

Círculo círculo; à Como Java é case-sensitive pode-se declarar desta forma. //Esta sentença cria uma variável círculo do tipo Círculo.

Entretanto, isso não é suficiente para acessarmos os métodos e atributos públicos da classe. A sentença acima somente declara uma variável, mas não cria um objeto da classe especificada. Em Java, objetos são criados usando o operador new da seguinte forma:

Círculo círculo; círculo = new Círculo();

O operador new cria uma instância da classe e retorna a referência do novo objeto.

Como vimos, todos os objetos em Java são tratados através da referência ao endereço de memória onde o objeto está armazenado. O operador new realiza três tarefas:

  1. Aloca memória para o novo objeto;
  2. Chama um método especial de inicialização da classe chamado construtor;
  3. Retorna a referência para o novo objeto;

É importante compreender o que ocorre na declaração da variável e na inicialização da variável. Quando fazemos

é reservado uma porção da memória principal do Java (stack) para armazenar o endereço na memória auxiliar (heap) onde o objeto será armazenado. Como apenas com a declaração da variável o objeto ainda não existe, o conteúdo inicial dela será o valor nulo (null), indicando que ela ainda não se refere a nenhum objeto.

Apenas após a inicialização é que uma variável de um tipo não primitivo estará valendo algo e através dela será possível acessar os dados e operações do objeto em questão.

Uma vez um objeto tendo sido criado, seus métodos e atributos públicos podem ser acessados utilizando o identificador do objeto (variável que armazena sua referência) através do operador ponto:

. . A aplicação que criou o objeto Círculo acima pode solicitar ao objeto que ele se desenhe fazendo: círculo.criaCírculo();

Polimorfismo

O termo Polimorfismo origina-se do grego e quer dizer "o que possui várias formas".

Em programação está relacionado à possibilidade de se usar o mesmo nome para métodos diferentes e à capacidade que o programa tem em discernir, dentre os métodos homônimos, aquele que deve ser executado. De maneira geral o polimorfismo permite a criação de programas mais claros, pois elimina a necessidade de darmos nomes diferentes para métodos que conceitualmente fazem a mesma coisa, e também programas mais flexíveis, pois facilita em muito a extensão dos mesmos.

O polimorfismo pode ser de duas formas, estático ou dinâmico:

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados