Classes Abstratas
e
Interfaces
Paulo Borba
Centro de Informática
Universidade Federal de Pernambuco
Objeto Conta Imposto
creditar
saldo
numero
875,32 21.342-7
debitar
Estados do Objeto Conta
Imposto
debitar(20)
creditar
Crédito
saldo
numero
getSaldo
Número
creditar
Crédito
saldo
numero
getSaldo
Número
875,00 21.342-7
854,98 21.342-7
debitar
debitar
875,32
21.342-7
Débito
875,32
21.342-7
Débito
Conta Imposto: Assinatura
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
public class ContaImpostoM extends Conta {
public ContaImpostoM(String numero) {}
}
Conta Imposto: Descrição
public class ContaImpostoM extends Conta {
private static final double taxa = 0.001;
public ContaImpostoM (String numero) {
super (numero);
}
public void debitar(double valor) {
double imposto = (valor * taxa);
super.debitar(valor + imposto);
}
}
Subtipos e Subclasses
ContaImposto
Conta
Subclasses e Comportamento



Objetos da subclasse comportam-se
como os objetos da superclasse
Redefinições de métodos devem
preservar o comportamento
(semântica) do método original
Grande impacto sobre
manutenção/evolução de software...
Revisão/Otimização de
Código
...
double m(Conta c) {
c.creditar(x);
c.debitar(x);
return
c.getSaldo();
}
...
...
double m(Conta c) {
return
c.getSaldo();
}
...
Modificação é correta? Em que contextos?
Subclasses e Evolução de
Software


Deveria ser possível raciocinar sobre
o código usando-se apenas a definição
dos tipos das variáveis envolvidas
(Conta)
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
Poupança
ContaImpostoM
ContaEspecial
Reuso preservando Subtipos
ContaAbstrata
Conta
Poupanca
ContaImposto
ContaEspecial
Definindo Classes Abstratas
public abstract class ContaAbstrata {
private String numero;
private double saldo;
public ContaAbstrata (String numero) {
this.numero = numero;
saldo = 0.0;
}
public void creditar(double valor) {
saldo = saldo + valor;
}
/* ... */
Definindo Classes Abstratas
/* ... */
public abstract void debitar(double valor);
protected void setSaldo(double saldo) {
this.saldo = saldo;
}
public double getSaldo() {
return saldo;
}
/* ... */
}
Classes Abstratas


Possibilita herança de código preservando
comportamento (semântica)
Métodos abstratos:
• Geralmente existe pelo menos um
• São implementados nas subclasses

Não cria-se objetos:
• Mas podem (devem) ter construtores, para reuso
• Métodos qualificados como 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(getSaldo() - valor);
}
}
Poupanças: Descrição
Original
public class Poupanca extends Conta {
public Poupanca(String numero) {
super (numero);
}
public void renderJuros(double taxa) {
this.creditar(getSaldo() * taxa);
}
}
Conta Especial: Descrição
Original
public class ContaEspecial extends Conta {
public static final double taxa = 0.01;
private double bonus;
public ContaEspecial (String numero) {
super (n);
}
public void creditar(double valor) {
bonus = bonus + (valor * taxa);
super.creditar(valor);
}
/* ... */
}
Conta Imposto: Descrição
public class ContaImposto extends ContaAbstrata {
public static final double taxa = 0.001;
public ContaImposto (String numero) {
super (numero);
}
public void debitar(double valor) {
double imposto = valor * taxa;
double total = valor + imposto;
this.setSaldo(getSaldo() – total);
}
}
Substituição e Ligações
Dinâmicas
...
ContaAbstrata ca, ca’;
ca = new ContaEspecial(¨21.342-7¨);
ca’ = new ContaImposto(¨21.987-8¨);
ca.debitar(500);
ca’.debitar(500);
System.out.println(ca.getSaldo());
System.out.println(ca’.getSaldo());
...
Classes Abstratas:
Utilização



Herdar código sem quebrar noção de
subtipos, preservando o
comportamento do supertipo
Generalizar código, através da
abstração de detalhes não relevantes
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;
}
/* ... */
}
Cliente: Projeto OO
public abstract class Cliente {
private String nome;
private ConjuntoContato contatos;
/* ... */
public void incluirContato(Contato contato) {
contatos.incluir(contato);
}
public abstract Endereco getEndereco();
public abstract Contato getContato(String tipo);
/* ... */
}
Contato: Reuso e Subtipos
Contato
Endereco
Telefone
EndEletronico EndPostal
Contato: Projeto OO
public abstract class Contato {
private String tipo;
public Contato (String tipo) {
this.tipo = tipo;
}
public abstract String getInfoRotulo();
}
Endereço: Projeto OO
public abstract class Endereco extends Contato {
public Endereco (String tipo) {
super (tipo);
}
}
Endereço Eletrônico: Projeto
OO
public class EnderecoEletronico extends Endereco {
private String email;
public EnderecoEletronico(String email) {
super (“EnderecoEletronico”);
this.email = email;
}
public String getInfoRotulo() {
return (“Email: ” + email);
}
}
Endereço Residencial:
Projeto
public class EnderecoPostal extends
Endereco {
private String rua;
private String cidade;
// ...
public EnderecoPostal(String cidade,
String rua,/*...*/) {
super (“EnderecoPostal”);
this.cidade = cidade;
this.rua = rua;
// ...
}
public String getInfoRotulo() {
return (“Rua: ” + rua /*...*/);
}
}
Telefone: Projeto
public class Telefone extends Contato {
private String ddd;
private String numero;
public Telefone(String ddd, String numero) {
super (“Telefone”);
this.numero = numero;
this.ddd = ddd;
}
public String getInfoRotulo() {
return (“DDD: ” + ddd +
“Numero: “ + numero);
}
}
Auditor de Banco
public class AuditorB {
private final static double MINIMO = 500.00;
private String nome;
/* ... */
public boolean auditarBanco(Banco banco) {
double saldoTotal, saldoMedio;
int numeroContas;
saldoTotal = banco.saldoTotal()
numeroContas = banco.numeroContas();
saldoMedio = saldoTotal/numeroContas;
return (saldoMedio < MINIMO);
}
}
Auditor de Banco Modular
public class AuditorBM {
private final static double MINIMO = 500.00;
private String nome;
/* ... */
public boolean auditarBanco(BancoModular banco)
{
double saldoTotal, saldoMedio;
int numeroContas;
saldoTotal = banco.saldoTotal()
numeroContas = banco.numeroContas();
saldoMedio = saldoTotal/numeroContas;
return (saldoMedio < MINIMO);
}
}
Problema


Duplicação desnecessária de código
O mesmo auditor deveria ser capaz de
investigar qualquer tipo de banco que
possua operações para calcular
• o número de contas, e
• o saldo total de todas as contas.
Auditor Genérico
public class AuditorGenerico {
private final static double MINIMO = 500.00;
private String nome;
/* ... */
public boolean auditarBanco(QualquerBanco banco)
{
double saldoTotal, saldoMedio;
int numeroContas;
saldoTotal = banco.saldoTotal()
numeroContas = banco.numeroContas();
saldoMedio = saldoTotal/numeroContas;
return (saldoMedio < MINIMO);
}
}
Definindo Interfaces
public interface QualquerBanco {
double saldoTotal();
int numContas();
}
Interfaces

Caso especial de classes abstratas...
• todos os métodos são abstratos
—provêem uma interface para serviços e
comportamentos
—são qualificados como public por default
• não definem atributos
—definem constantes
—por default todos os “atributos” definidos
em uma interface são qualificados como
public, static e final
• não definem construtores
Interfaces



Não pode-se criar objetos
Definem tipo de forma abstrata,
apenas indicando a assinatura dos
métodos
Os métodos são implementados pelos
subtipos (subclasses)
Subtipos sem Herança de
Código
public class Banco implements QualquerBanco {
/* ... */
}
public class BancoModular
implements QualquerBanco {
/* ... */
}
implements



classe implements
interface1, interface2, ...
subtipo implements
supertipo1, supertipo2, ...
Múltiplos supertipos:
• uma classe pode implementar mais de
uma interface (contraste com classes
abstratas...)
implements

Classe que implementa uma interface
deve definir os métodos da interface:
• classes concretas têm que implementar
os métodos
• classes abstratas podem simplesmente
conter métodos abstratos
correspondentes aos métodos da
interface
Usando Auditores
Banco b = new Banco();
BancoModular bm = new BancoModular();
Auditor a = new Auditor();
/* ... */
boolean r = a.auditarBanco(b);
boolean r’ = a. auditarBanco(bm);
/* ... */
Interfaces e Reusabilidade
• Evita duplicação de código através da
definição de um tipo genérico, tendo como
subtipos várias classes não relacionadas
• Tipo genérico pode agrupar objetos de
várias classes definidas de forma
independente, sem compartilhar código via
herança, tendo implementações totalmente
diferentes
• Classes podem até ter mesma semântica...
Definição de Classes: Forma
Geral
class C’
extends C
implements I1, I2, ..., In {
/* ... */
}
I1
I2 C
C’
... In
Subtipos com Herança
Múltipla de Assinatura
interface I
extends I1, I2, ..., In {
/*... assinaturas de novos
métodos ...
*/
}
O que usar? Quando?
Classes (abstratas)

Agrupa objetos com
implementações
compartilhadas

Define novas classes
através de herança
(simples) de código

Só uma pode ser
supertipo de outra
classe
Interfaces

Agrupa objetos com
implementações
diferentes

Define novas interfaces
através de herança
(múltipla) de assinaturas

Várias podem ser
supertipo do mesmo tipo
Cadastro de Contas:
Parametrização
public class CadastroContas {
private RepositorioContas contas;
public CadastroContas (RepositorioContas r) {
if (r != null) contas = r;
}
/* ... */
}
A estrutura para armazenamento das contas é
fornecida na inicialização do cadastro,
e pode depois ser trocada!
Repositório: Definição
public interface RepositorioContas {
void inserir(Conta conta);
Conta procurar(String numero);
boolean existe(String numero);
}
Repositório: Implementações
public class ConjuntoContas
implements RepositorioContas {...}
public class ListaContas
implements RepositorioContas {...}
public class ArrayContas
implements RepositorioContas {...}
public class VectorContas
implements RepositorioContas {...}
Cadastro de Contas:
Parametrização
public void cadastrar(Conta conta) {
if (conta != null) {
String numero = conta.getNumero();
if (!contas.existe(numero)) {
contas.inserir(conta);
}
}
}
Cadastro de Contas:
Parametrização
public void debitar(String numero, double valor){
Conta conta;
conta = contas.procurar(numero);
if (conta != null) {
conta.debitar(val);
}
}
Download

Classes Abstratas - Centro de Informática da UFPE