Demais partes desse curso:
Orientação a Objetos - simples assim! - Parte 1

Essa é a segunda parte da séria orientação a objetos, simples assim! Abordaremos: Herança, Override, Overload.

O objetivo desse artigo é dá ao leitor – iniciante em Java - um “start” nos estudos de OO, de forma didática e prática. Após essa leitura recomendo um aprofundamento em materiais e livros citados ao fim desse documento.

É importante que o leitor tenha acompanhado o artigo anterior – OO parte I.

Continuaremos a implementação do projeto piloto iniciado no artigo parte I: Bufunfa.

Bom, antes de mais nada, vamos implementar os métodos da classe Conta e criar mais alguns. O que está em Laranja é o que há de novo em relação ao que já tínhamos feito no artigo parte I. Veja a Código 3.1.

package br.com.fw3;
public class Conta {  
      private int numeroConta;
      private String titular;  
      //representa o saldo atual dessa conta
      protected double saldo;
//construtor
public Conta(int numeroConta, String titular) {
this.numeroConta = numeroConta;
this.titular = titular;
}
//saca o valor da conta e retorna true se operação bem sucedido
      public boolean sacar(double valor) {
if (valor < saldo) {
this.saldo -= valor;
   return true;
}
else {
   return false;
}
}
      //deposita o valor na conta e retorna true se operação bem sucedido
      public boolean depositar(double valor) {
this.saldo += valor;
return true;
     }
      //recebe como parâmetro a contaDestino, a qual sofrerá um  crédito do valor.
      //ao mesmo tempo, esse mesmo valor deverá ser debitado da conta origem
      public boolean transferir(Conta contaDestino, double valor){
        boolean retirou = sacar(valor);
if (retirou) {
   contaDestino.depositar(valor);
   return true;
}
else {
   return false;
}
      }
//retorna o saldo da conta
public double getSaldo() {
return this.saldo;
}
public void gerarTaxa() {}
}
Código 3.1. – Classe Conta

Observe que criamos mais um método: gerarTaxa(). Esse comportamento está sem funcionalidade, ou seja, não implementamos nada nele. E pra que serve? Trata-se de um método genérico que deverá ser implementado por seus herdeiros...

Genética? Objetos também herdam?

Pois é, herdam sim. Um dos recursos mais importantes da orientação a objetos é a herança. E isso não é uma particularidade do Java, esse conceito é bem antigo... Bom, vamos ao que interessa.

Através da herança uma classe pode reutilizar códigos já implementados em sua classe pai, essa última chamamos de superclasse. Essa classe que aproveitou (ou herdou) alguma coisa da superclasse chamamos de subclasse.

A Figura 3.1 ilustra uma situação comum em sistemas.


fsoopp3fig01.jpg
Figura 3.1. Exemplo de Herança

O diagrama de classe apresentado na Figura 3.1 modela 3 classes, onde duas – as de baixo – herdam de outra mais genérica – a de cima. As linhas, que interligam as classes de baixo a classe Cliente orientada por uma seta não preenchida, são a representação da UML para herança.

Observe a superclasse Cliente, ela define atributos genéricos de um cliente para um determinado sistema. Já as subclasses ClientePessoaFisica e ClientePessoaJuridica definem atributos específicos as suas respectivas naturezas, e ao mesmo tempo herdam os atributos de Cliente (endereco, bairro, telefone, ultimaCompra), que são protected (#), ou seja, visível às suas subclasses. Dessa forma construímos um modelo que além de reutilizar código, está semanticamente mais estruturado do que se não utilizássemos herança. Reutiliza código por que não foi necessário reescrever os atributos comuns nas subclasses. E está semanticamente mais estruturado por que ClientePessoaFisica e ClientePessoaJuridica são, por natureza, Clientes – classe Cliente – isso torna o sistema mais flexível e expansível.

Vale lembrar que assim como os atributos, os métodos também podem ser herdados. Vamos supor que a classe Cliente possua um método público (public) chamado getEndereco() que retorna uma String contendo o endereço daquele cliente. Podemos, nesse caso, afirmar sem medo de errar, que as classes ClientePessoaFisica e ClientePessoaJuridica também possuem esse método, pois foi herdado.

Peço-lhe paciência, ainda nessa leitura tudo ficará mais claro.

Com o conhecimento até aqui adquirido podemos evoluir nosso pequeno projeto começado no artigo parte II. Naquele momento criamos uma classe Conta, conforme mostra a Figura 3.2

fsoopp3fig02.jpg

Figura 3.2. Classe Conta do projeto Bufunfa.

Os requisitos solicitavam lógicas específicas para Conta Poupança e Conta Corrente.

A classe da Figura 3.2 representa uma conta bancária genérica. Mas temos que especializar nosso negócio, ou seja, precisamos criar mais duas classes que tratem, separadamente, da lógica de Poupança e Conta Corrente, que apesar de serem contas, possuem comportamentos – métodos - diferenciados. A Figura 3.3 ilustra a modelagem para essa nova abordagem.

fsoopp3fig03.jpg

Figura 3.3. Classe Conta e suas subclasses.

Pronto, agora temos as classes ContaPoupanca e ContaCorrente que herdam de Conta. Subtende-se, que as subclasses possuem todos os atributos e métodos da sua superclasse – Conta - excetuando-se membros privados (private).

A criação das subclasses, descritas na Figura 3.3, utiliza-se da palavra reservada extends para determinar que uma classe herde outra: Veja as Listagens 3.2 e 3.3.

public class ContaPoupanca extends Conta {
    ...
 }
Código 3.2. Classe ContaPoupanca herdando de Conta.
public class ContaCorrente extends Conta {
    ...
 }
Código 3.3. Classe ContaCorrente herdando de Conta.

fsoopp3fig04.jpg

PODERÍAMOS NOS ALONGAR MAIS SOBRE HERANÇA, PORÉM MENCIONAMOS O QUE BASTA ATÉ ESSE MOMENTO.

Objetos transgênicos? Override e overload

Bom..., por que não? Também podemos interferir na “genética dos objetos”.

Como definimos anteriormente, a Poupança e a Conta Corrente terão alguns comportamentos diferenciados. Um exemplo de diferença: A Poupança gera rendimentos, enquanto a Conta Corrente gera CPMF e taxas de débito.

Mas como alterar os comportamentos herdados da classe Conta?

Bom, pra isso existe o override, que permite sobrescrever um método.

Como faço um override? Muito simples... Basta assinar, na subclasse, o método que deseja sobrescrever tal qual foi assinado na superclasse. Dessa forma, vamos fazer um override (sobrescrever) do método gerarTaxa() da classe Conta na classe ContaPoupanca. Veja a Código 3.4.

package br.com.fw3;
  
 public class ContaPoupanca extends Conta {
  
   public ContaPoupanca (int numeroConta, String titular) {
      super(numeroConta, titular);
   }
   
   public void gerarTaxa() {
     saldo += saldo * .006;
   }
 }
Código 3.4. Override do método gerarTaxa() na classe ContaPoupanca.

Pronto! A nossa Poupança agora está gerando juros positivos, pois reescrevemos o método de geração de taxas para acrescentar 0,6% - que é o rendimento da poupança - ao saldo da conta.

E visto que, o ladrão do nosso banco cobra uma taxa mensal de administração de R$ 15,90 para quem possui uma Conta Corrente... Nossa classe ContaCorrente precisará sobrescrever (override) o método gerarTaxa(), conforme mostrado na Código 3.5.

package br.com.fw3;
  
 public class ContaCorrente extends Conta {
  
   public ContaCorrente (int numeroConta, String titular) {
      super(numeroConta, titular);
   }
   
   public void gerarTaxa() {
     saldo -= 15.90;
   }
 }
Código 3.5. Override do método gerarTaxa() na classe ContaCorrente.

Pronto!

Tem mais um ajuste interessante. Nosso banco esclareceu que, para alguns clientes de Conta Corrente, será cobrado a CPMF – 0,38% do valor sacado. E para outros clientes, especiais, essa taxa não será cobrada.

Para isso vamos sobrecarregar (overload) o método sacar. Sobrecarregar significa criar outro método com o mesmo nome, porém com tipos de parâmetros diferentes ou em ordem distinta. Assim, poderemos oferecer, ao cliente, várias formas de utilizar um mesmo serviço.

Pela estrutura de linguagem do Java, podemos sobrecarregar métodos tanto da própria classe como das superclasses (pai, avo, etc). Já no override é diferente, somente podemos fazer uma sobrescrita de métodos que estejam, originalmente, na superclasse.

A Código 3.6 demonstra como ficará o método sacar.

package br.com.fw3;
  
 public class ContaCorrente extends Conta {
  
   public ContaCorrente (int numeroConta, String titular) {
      super(numeroConta, titular);
   }
   
   public void gerarTaxa() {
     saldo -= 15.90;
   }
  
   public boolean sacar(double valor, boolean cobraCPMF){ 
     if (cobraCPMF) {
       return sacar(valor + valor * .0038);
     }
     else {
       return sacar(valor);
     }
   }
 }
Código 3.6. Overload do método sacar da classe ContaCorrente.

Agora é hora da revisão:

§ Override (ou sobrescrita):

  • Para que serve: Sobrescreve um método que foi originalmente implementado na superclasse (pai, avo, etc).
  • Como se aplica: Deve ter o mesmo tipo de retorno, mesmo nome, e parâmetros dos mesmos tipos na mesma ordem. Os nomes dos parâmetros não importam.
  • Restrições: Não pode sobrescrever método na mesma classe. Existem outras restrições quanto à visibilidade e exceções que não são contempladas pelo escopo desse artigo.

§ Overload (ou sobrecarga):

  • Para que serve: Cria diversas formas de oferecer um serviço com o mesmo nome.
  • Como se aplica: Basta possuir o mesmo nome e tipos de parâmetros diferentes ou em outra ordem. Os nomes dos parâmetros não importam.
  • Importante: A simples alteração do tipo de retorno do método, não se trata de um overload, se sim de um erro de sintaxe.

Por hoje é .

Resumindo. O que foi visto nesse artigo:

  • o Herança
  • o Override
  • o Overload

Material para iniciante

Livro: OOP DESMISTIFICADO - PROGRAMAÇÃO ORIENTADA A OBJETOS por JIM KEOGH, MARIO GRANNINI