classes abstratas e interfaces aula 12 - ufpeif669/material/aulasnovas... · subclasses e...
TRANSCRIPT
Ricardo Massa F. Lima [email protected]
Sérgio C. B. Soares [email protected]
Classes Abstratas e Interfaces
AULA 12
Introdução a Programação – IF669 http://www.cin.ufpe.br/~if669
Até aqui n Quando usar herança?
n Ao redefinir um método manter o comportamento herdado!
Adivinhem... n Surge um novo requisito na aplicação
bancária
n Temos de cobrar um imposto em certos tipos de contas • Lembram da CPMF?
numero saldo
21.342-7 875,32
Objeto Conta Imposto
creditar
debitar
Número getSaldo
21.342-7 875,32
Crédito
Débito
Estados do Objeto Conta Imposto
numero saldo
21.342-7 875,00
debitar(20)
creditar
debitar
Número getSaldo
21.342-7 875,32
Crédito
Débito
numero saldo
21.342-7 854,98
creditar
debitar
Conta Imposto: Assinatura Sem herança
public class ContaImposto { public ContaImposto (String numero) {} public void creditar(double valor) {} public void debitar(double valor) {} public String getNumero() {} public double getSaldo() {} }
Conta Imposto: Assinatura Com herança
public class ContaImpostoM extends Conta { public ContaImpostoM(String numero) {} public void debitar(double valor) {} }
Conta Imposto: Descrição Com herança
public class ContaImpostoM extends Conta { private static final double CPMF = 0.001; public ContaImpostoM (String numero) { super (numero); } public void debitar(double valor) { double imposto = (valor * CPMF); super.debitar(valor + imposto); } }
Subtipos e Subclasses ContaImposto
Conta
Subclasses e Comportamento (1)
n Objetos da subclasse devem se comportar como os objetos da superclasse • Afinal de contas queremos usar objetos da
subclasse onde os objetos da superclasse são utilizados
public class Banco { private Conta[] contas; private int indice; // ... }
n Redefinições de métodos devem preservar o comportamento (semântica) do método original • No que diz respeito ao comportamento (e
atributos) herdado n Grande impacto sobre manutenção/
evolução de software...
Subclasses e Comportamento (2)
Revisão/Otimização de Código
... double m(Conta c) { c.creditar(10); c.debitar(10); return c.getSaldo(); } ...
Considerando apenas o retorno do método m, as duas opcões são sempre equivalentes?
Em que contextos?
... double m(Conta c) { return c.getSaldo(); } ...
Subclasses e Evolução de Software
n Deveria ser possível raciocinar sobre o código usando-se apenas a definição dos tipos das variáveis envolvidas (Conta)
n O comportamento do código deveria ser independente do tipo do objeto (Conta, ContaEspecial, ContaImposto) associado a uma dada variável em tempo de execução
Reuso sem Subtipos
Conta
Poupanca ContaImpostoM
ContaEspecial
ContaImpostoM muda a semântica do método debitar e, se herdar de Conta, quebra a noção de subtipos!!!
Qual a alternativa então? n O que existe de comum entre Conta e ContaImposto? • Vamos criar uma nova classe que
contenha essa parte em comum • Conta e ContaImposto devem herdar
dessa nova classe n Atenção: debitar é diferente nas duas
classes • Mas ambas as contas devem permitir
debitar um valor ...
Reuso preservando Subtipos ContaAbstrata
ContaImposto Conta
Poupanca ContaEspecial
Definindo Classes Abstratas public abstract class ContaAbstrata { private String numero; private double saldo; public ContaAbstrata (String numero) { this.numero = numero; this.saldo = 0.0; } public void creditar(double valor) { this.saldo = this.saldo + valor; }
public double getSaldo() {
return this.saldo;
}
public String getNumero() { return this.numero; } protected void setSaldo(double saldo) { this.saldo = saldo; }
public abstract void debitar(double valor); }
O método abstrato não tem implementação, mas 1. Permite programar (não executar), chamando o método da
classe abstrata (na classe Banco, por exemplo) 2. Obriga que as subclasses concretas implementem o método
Definindo Classes Abstratas
Revisão/Otimização de Código
E agora, a modificação é correta? Em que contextos?
... double m(ContaA c) { c.creditar(10); c.debitar(10); return c.getSaldo(); } ...
... double m(ContaA c) { return c.getSaldo(); } ...
Classes Abstratas n Possibilita herança de código preservando
comportamento (semântica) • Não do método debitar, que ainda não possui
comportamento n Métodos abstratos:
• geralmente, existe pelo menos um • são implementados nas subclasses
n Não se cria objetos: • mas devem ter construtores para reuso • se necessário, métodos protected para serem
acessados nas subclasses
Contas: Descrição Modificada
public class Conta extends ContaAbstrata { public Conta(String numero) { super (numero); } public void debitar(double valor) { this.setSaldo(this.getSaldo() - valor); } }
Implementação do método abstrato observe o uso do método protected
Poupanças: Descrição Original
public class Poupanca extends Conta { public Poupanca(String numero) { super (numero); } public void renderJuros(double taxa) { this.creditar(this.getSaldo() * taxa); } }
Nada mudou para Poupanca
Conta Especial: Descrição Original
public class ContaEspecial extends Conta { private static final double TAXA = 0.01; private double bonus; public ContaEspecial (String numero) { super(numero); this.bonus = 0.0; } public void creditar(double valor) { this.bonus = this.bonus + (valor * TAXA); super.creditar(valor); } } Nada mudou para ContaEspecial
Conta Imposto: Descrição public class ContaImposto extends ContaAbstrata { private static final double CPMF = 0.001; public ContaImposto (String numero) { super(numero); } public void debitar(double valor) { double imposto = valor * CPMF; double total = valor + imposto; super.setSaldo(this.getSaldo() – total); } }
Implementação do método abstrato observe o uso do método protected
Substituição e Ligações Dinâmicas
ContaAbstrata ca1, ca2; ca1 = new ContaEspecial(“21.342-7”); ca2 = new ContaImposto(“21.987-8”); ca1.debitar(500); ca2.debitar(500); System.out.println(ca1.getSaldo()); System.out.println(ca2.getSaldo());
Exemplo de um trecho do método main, usado para teste
Classes Abstratas: Utilização n Herdar código sem quebrar noção de
subtipos, preservando o comportamento do supertipo
n Generalizar código, através da abstração de detalhes não relevantes
n Projetar sistemas, definindo as suas arquiteturas e servindo de base para a implementação progressiva dos mesmos
Contas: Projeto OO public abstract class ContaProjeto { private String numero; private double saldo; //... public abstract void creditar(double valor); public abstract void debitar(double valor); public String getNumero() { return numero; protected setSaldo(double saldo) { this.saldo = saldo; } //... }
Outro exemplo Pessoa: Reuso e Subtipos
Pessoa
PessoaFisica PessoaJuridica
Pessoa: Projeto OO
public abstract class Pessoa { private String nome; ... public abstract String getCodigo(); }
public class PessoaFisica extends Pessoa { private String cpf; ... public String getCodigo() { return cpf; } }
Pessoa Física: Projeto OO
public class PessoaJuridica extends Pessoa { private String cnpj; ... public String getCodigo() { return cnpj; } }
Pessoa Jurídica: Projeto OO
public class RepositorioPessoasArray { private Pessoa[] pessoas; ... public Pessoa procurar(String codigo) { Pessoa p = null; boolean achou = false; for (int i=0; i<indice && !achou; i++) { p = pessoas[i]; if (p.getCodigo().equals(codigo)) achou = true; else p = null; } return p; } }
Exercício n Utilize a solução do último exercício
http://www.cin.ufpe.br/~if669/material/solucoes/aula12.zip n Defina no pacote aula13.br.ufpe.cin.banco, a classe abstrata
ContaAbstrata que tem os mesmos atributos e métodos de Conta, só que o método debitar é abstrato, como visto em sala
n Altere a classe Conta para herdar da classe ContaAbstrata e implementar o método debitar
n Modifique a classe Banco para que seja possível trabalhar com todos os tipos de conta da aplicação bancária. Execute a classe Programa e observe que o teste funciona como antes
n Defina a classe aula13.br.ufpe.cin.banco.ContaImposto que herda de ContaAbstrata e tem uma constante CPMF que armazena o imposto a ser cobrado quando um valor for debitado na ContaImposto
n É necessário alterar as classes Poupanca e ContaEspecial? n Altere a classe Programa para testar todas as classes do projeto
Interfaces
Encapsulamento e Information Hiding n A habilidade em “esconder” de forma
segura dados e métodos de uma classe dentro da “cápsula”da classe, impedindo acesso de usuários não confiáveis é conhecida como information hiding
mas...por que estamos interessados em fazer isso?
Por que ecapsulamento com information hiding é útil?
n Esconder detalhes de implementação • evita que outros programadores façam
uso dessas informações com algum propósito
• torna possível modificar a implementação com a segurança de que não afetará o código que utiliza a classe
n Protege a classe de interferências externas indesejáveis (sejam elas acidentais ou propositais) • A classe contém conjunto de campos
interdependentes, que devem ser mantidos em um estado consistente
• Se for permitido a algum usuário externo (ou você mesmo) modificar um campo sem modificar campos relacionados a ele, a classe ficará em um estado inconsistente
• Se, por outro lado, o acesso é feito via um método, há mais chances de que o estado será mudado de maneira consistente
Por que ecapsulamento com information hiding é útil?
n Se todos os dados da classe estão escondidos e podem ser acessados apenas via métodos da classe e esses métodos foram bem testados, haverá maior garantia de que os dados serão modificados consistentemente
n Se, por outro lado, for permitido acesso externo, o número de possibilidades a serem testadas torna-se não gerenciável.
Por que ecapsulamento com information hiding é útil?
n Se atributos públicos que são acessados diretamente por outras classes forem modificados, as classes que os acessam diretamente serão afetadas • Acessando os mesmos com métodos pode evitar
esse impacto n Se um determinado método for definido
apenas para uso interno da classe, esconder esse método evita que usuários da classe tentem usá-lo
Por que ecapsulamento com information hiding é útil?
Menos nobre, mas... se um campo ou método de uma classe for visível, você terá que
documentá-lo
economize seu tempo e esforço!
esconda os campos e métodos ao máximo!
Por que ecapsulamento com information hiding é útil?
Interfaces n Através do encapsulamento, os atributos e a
implementação dos métodos de uma certa classe não são visíveis ao usuário da classe
n Conhecendo-se apenas a interface de uma classe, podemos utilizar seus objetos sem conhecer detalhes de implementação
n Uma interface inclui os métodos disponíveis e suas respectivas assinaturas
n Além disto, existem casos onde existe a necessidade de se ter uma classe mas não queremos implementá-la • pode-se terceirizar a implementação, fornecendo como
especificação a interface desejada.
Interfaces - Exemplo n Implementar um zoológico virtual com vários tipos de animais
n Você gostaria de enviar as seguintes mensagens a cada animal: • nasca()• passeie()• durma()• peso()
n Vamos pedir ajuda a programadores especialistas em cada tipo de animal
Interfaces - Exemplo
Interfaces - Exemplo n O programador que for implementar o morcego terá que dizer
explicitamente que vai usar a interface Animal • palavra chave implements
A palavra chave implements obriga o programador a escrever o código de todos os métodos na assinatura
Todos os métodos da interface devem ser públicos
Interfaces - Exemplo
Interfaces - Exemplo
Interfaces - Observação Em cada arquivo deve existir no máximo uma classe pública! Logo, as classes Ornitorrinco, Morcego e Zebra devem estar em arquivos separados, com os respectivos nomes
Ornitorrinco.java Zebra.java Morcego.java
Interfaces Cada um dos animais, além de ser um objeto da própria classe, também é um objeto do tipo Animal
z2.contaLi
stras() -
Inválido
z1.contaLi
stras() -
Válido
Implementando mais de uma interface por vez n Considere as duas interface:
n Vamos implementar essas três classes
Implementando mais de uma interface por vez
Implementando mais de uma interface por vez
Finalmente, podemos ver o Aviao que implementa as duas interfaces:
Mas e na aplicação bancária? Onde usar interfaces?
n Hoje a classe Banco tem um array de Conta
n E se amanhã quisermos utilizar outra estrutura de dados?
n E se quisermos depois de amanhã utilizar um banco de dados?
n Vamos desacoplar as regras de negócio do Banco de onde as contas são armazenadas
Criar uma interface de armazenamento de dados public interface RepositorioContas { void inserir(ContaAbstrata conta); ContaAbstrata procurar(String numero); void remover(String numero); void atualizar(ContaAbstrata conta); boolean existe(String numero); }
Todos os métodos são public e abstract por default e não se definem atributos nem construtores
Repositório: Implementações public class RepositorioContasArray implements RepositorioContas {...} public class RepositorioContasLista implements RepositorioContas {...} public class RepositorioContasVector implements RepositorioContas {...} public class RepositorioContasBDR implements RepositorioContas {...}
Banco: Parametrização public class Banco { private RepositorioContas contas; public Banco(RepositorioContas rep){ this.contas = rep; } public void cadastrar(ContaAbstrata conta){ String numero = conta.getNumero(); if (!contas.existe(numero)) { contas.inserir(conta); } else { throw new RuntimeException(“Já cad...”); } // ... }
A estrutura para armazenamento das contas é fornecida na inicialização do banco, e pode ser trocada!
O que usar? Quando? Classes (abstratas) n Agrupa objetos com
implementações compartilhadas
n Define novas classes através de herança (simples) de código
n Uma classe pode ter apenas uma como superclasse
Interfaces n Agrupa objetos com
implementações diferentes
n Define novas interfaces através de herança (múltipla) de assinaturas
n Uma classe pode ter várias como supertipo
n Utilize a solução dos exercícios da última aula http://www.cin.ufpe.br/~if669/material/solucoes/aula13.zip
n Defina no pacote aula14.br.ufpe.cin.dados a interface RepositorioContas com os métodos • inserir – recebe uma ContaAbstrata e insere no repositório • procurar – recebe um número e retorna a conta se estiver no repositório • remover – recebe um número para remover a conta do repositório • atualizar - recebe uma ContaAbstrata para atualizar no repositório • existe – recebe um número e informa se existe uma conta com este
número no repositório n Modifique a classe Banco para utilizar a interface definida
(receba a implementação da interface no construtor) • Perceba que a classe Banco compila apenas com a interface. Já a
classe Programa precisa de uma implementação para executar. n Defina no pacote aula14.br.ufpe.cin.dados classe
RepositorioContasArray que implementa a interface RepositorioContas
n Altere a classe Programa para fazer os testes
Exercício