Rapidissímo overview
IFs e switches X herança
Vantagens e desvantagens do uso de Herança Casos errados do uso de herança
Herança versus Composição Herança versus Interfaces
Programando pensando em Interfaces
Antes de qualquer coisa:
Não estou falando mal do seu código!
Switchs e IFs
Usando polimorfismo
Caso trivial
class Funcionario { private int codigo;
public static final int DIRETOR = 1;
public static final int PRESIDENTE = 2;
// ....
public int getCodigo() { return this.codigo;
}
}
Em algum outro lugar..
class Relatorio {
private double dinheiroGasto;
private Empresa empresa; //....
public void adiciona(Funcionario f) { switch (f.getCodigo()) {
case Funcionario.DIRETOR :
this.gastos += 1.1 * f.getSalario();
break;
case Funcionario.PRESIDENTE :
this.gastos += f.getSalario() +
empresa.getParteDosLucros();
break;
} }
}
Esse tipo de código vai se espalhar por toda aplicação!
Ao criar uma nova filha de Funcionario, precisamos mudar esse(s) código(s)!
Remodelando
class Funcionario {
public double getGastos() { return this.salario;
} }
class Diretor
extends Funcionario {
public double getGastos() { return this.salario * 1.1;
} }
Funcionario
Diretor Presidente
Cálculo dos gastos concentrado dentro da própria classe
E o relatório...
class Relatorio {
private double dinheiroGasto;
private Empresa empresa; //....
public void adiciona(Funcionario f) { this.gastos += f.getGastos();
} }
Para adicionar uma nova classe que extende Funcionario, não teremos mais de mexer na classe Relatorio!
Controle de Permissões
class Usuario { private int id;
private String name;
// ...
}
Usuario
Moderador Administrador O uso de herança nesse
caso é altamente dicutível!
Estamos usando apenas como exemplo.
Primeira tentativa
Ifs encadeados num estilo switch(int)public void apagaRegistro() {
if(this.user instanceof Usuario) { return false;
}
if(this.user instanceof Moderador) { return false;
}
if(this.user instanceof Administrador) { return true;
} }
Caso uma classe Colaborador fosse adicionada, precisaríamos mexer aqui também!
Usando polimorfismo
Usuario
Moderador Administrador
class Usuario {
public boolean hasRight(int code) { }
}
Apagando com a nova forma
public void apagaRegistro() {
if(this.user instanceof Usuario) { return false;
}
if(this.user instanceof Moderador) { return false;
}
if(this.user instanceof Administrador) { return true;
} }
Passa para:
public void apagaRegistro() {
if(this.user.hasRight(Permissions.DELETE){
return false;
} }
E ao criarmos a classe Colaborador...
class Colaborador extends Usuario {
public boolean hasRight(int code) { // monte de ifs e switchs ☺ }
}
Código de permissões não esta mais concetrado em um único lugar, cada classe é responsável pelo seu.
Pequena conclusão
Switch é uma anomalia
Polimorfismo é muito mais elegante
Você muda apenas na própria classe, não precisa concentrar lógica de negócio.
Switch é feio!
Isto não é uma regra rígida!
Vantagens da Herança
Herança, quais são as vantagens?
Reaproveitamento de código Possibilita polimorfismo
Vamos mostrar que podemos ter esses
benefícios de outras maneiras mais “leves”.
Quando não usar herança
Apredendo com os erros da Sun
O que vocês acham dessas classes?
java.util.Properties
java.util.Stack
Properties
public class Properties extends Hashtable
Properties é uma Hashtable?
Hashtable: associa Object – Object Properties: associa String - String
Properties não é uma Hashtable!
Properties
Object
Dictionary
Hashtable
Properties
O que acontece com esse código?
Object object = new Object();
Hashtable table = new Properties();
table.put(“x”, object);
table.get(“x”);
Funciona ok!
Um pouco pior....
Object object = new Object();
Properties table = new Properties();
table.put(“x”, object);
table.getProperty(“x”);
table.get(“x”);
Compila perfeitamente, mas só retorna o object quando chamado pelo .get, e pelo getProperty volta Null. Como adivinhariamos isso?
Parece então que são duas tabelas separadas. Uma para Object- Object, e outra para String String
Último round
Properties table = new Properties();
table.put(“x”, “y”);
table.getProperty(“x”);
Retorna null também?
Não, dessa vez acontece o “esperado”.
java.util.Stack
Caso parecido
Se é uma pilha, porque posso remove(int)?
Porque posso add(int, Object)?
Herdou apenas para não ter de escrever um vetor!
PREGUIÇA!
Pequena conclusão
Nunca usar herança quando a relação não é claramente “é um”.
Contratos acabam sendo quebrados Difícil de mudar depois
Pergunte a si mesmo se você não está fazendo
isso por pura preguiça!
Lembram-se desse caso?
Usuario
Moderador Administrador Moderador é um Usuario?
Usuario as vezes esta no papel de moderador ou
administrador, mas a relação não é exatamente “é um”.
Classe Entity
class Entity {
private int id;
private String name;
private String description;
// gets e sets }
class Usuario extends Entity {}
“é um” faz sentido? É um estado de Usuário? É um papel de Usuário?
Veremos como resolver isso com composição!
Problemas trazidos pela
herança
Considere
Funcionario
Diretor Presidente
Uso do protected
class Funcionario {
protected double salario;
protected int idade;
}
class Diretor extends Funcionario { public double getGastos() {
if(this.idade < 35) { return salario;
}
else {
return salario * 1.1;
} }
}
Uso do protected
class Funcionario {
protected double salario;
protected int idade;
}
class Funcionario {
protected double salario;
protected Calendar nascimento;
}
O que acontece com a classe Diretor?
Não compila? Isto não é o mais grave, ele poderia estar em outro jar, e ele vai
começar a lançar Errors
Uso do protected
Classe filha precisa conhecer bem a mãe
Classe mãe não pode mudar sua representação interna com facilidade! Quebra de encapsulamento.
Protected define um contrato forte para a mãe que não tem muito utilidade para quem esta de fora!
Métodos protected não são tão malvados assim
Considere
Pessoa
Pessoa Física
Pessoa Jurídica
Método na mãe muda
Quando um método da mãe muda pode
quebrar o comportamento da classe filha
Conhecemos a interface publica da classe
class PessoaJuridica {
public void adicionaNota(Nota nota) { }
public void adicionaVariasNotas(List<Nota> notas) { }
}
Não conhecemos bem essa classe!
Fornecedor quer saber quantas transcoes!
class FornecedorJuridica extends PessoalJuridica {
private int transacoes;
public void adicionaNota(Nota nota) { this.transacoes++;
super.adicionaNota(nota);
}
public void adicionaVariasNotas(List<Nota> notas) { this.transacoes += notas.size();
super.adicionaVariasNotas(notas);
}
public int getTransacoes() { return this.transacoes;
} }
List notas = ... // lista com 3 notas f.adicionaVariasNotas(notas);
f.getTotalTransacoes();
Funciona?
Depende!
class PessoaJuridica {
public void adicionaNota(Nota nota) { this.vetorzinho[posicao++] = nota;
}
public void adicionaVariasNotas(List<Nota> notas) { for(Nota n : notas) {
this.vetorzinho[posicao++] = n;
} }
}
List notas = ... // lista com 3 notas f.adicionaVariasNotas(notas);
f.getTotalTransacoes();
Resultado?
Mudança na classe mãe!
class PessoaJuridica {
public void adicionaNota(Nota nota) { this.vetorzinho[posicao++] = nota;
}
public void adicionaVariasNotas(List<Nota> notas) { for(Nota n : notas) {
// ...
this.adicionaNota(n);
} }
}
List notas = ... // lista com 3 notas f.adicionaVariasNotas(notas);
f.getTotalTransacoes();
E agora que mamãe mudou?
A resposta é 6!
Grande árvore de herança, mais problemas
Pessoa
Pessoa Física
Pessoa Jurídica
Fornecedor Jurídico Fornecedor
Físico
Herança, lados negativos
Você ganha mais coisas do que gostaria Sua relação com sua mãe é conturbada Quebra de encapsulamento
Não use herança apenas por
reaproveitamento de código da classe mãe!
Não use herança apenas pelo polimorfismo!
Herança versus Composição
Usando polimorfismo
Problemas! Além dos já apresentados:
Pessoa
Pessoa Física
Pessoa Jurídica
Fornecedor Jurídico Fornecedor
Físico Fornecedor
é
Pessoa?
Aqui tem código repetido!
FornecedorJuridico é Pessoa?
É sim, mas estamos utilizando algo como Pessoa p = new Fornecedor() ? Ou
recebendo Fornecedor como Pessoa?
Aumentado a hierarquia, como
representaríamos alguem como Fornecedor
e Consumidor?
Diminuindo acomplamento
Pessoa
Pessoa Física Fornecedor
Físico
class FornecedorFisico extends PessoaFisica {}
passa para:
class FornecedorFisico {
private PessoaFisica pessoa;
// metodos para delegar }
Acabando com código duplicado
Pessoa
Pessoa Física Fornecedor
Físico
Pessoa Jurídica
Fornecedor Jurídico
public String pegaNome() { }
public void enviaNotificacaoSobreCredito() { }
Acabando com código duplicado
Pessoa
Pessoa Física Fornecedor
Físico
Pessoa Jurídica
Fornecedor Jurídico
Fornecedor Helper
FornecedorHelper
Faz o trabalho comum aos Fornecedores Pode ser uma classe package-friendly
class FornecedorHelper { private String nome;
private String email;
public String pegaNome() { }
public void enviaNotificacaoSobreCredito() { }
}
class FornecedorJuridico {
private FornecedorHelper helper;
public void enviaNotificacaoSobreCredito() {
this.helper.enviaNotificacaoSobreCredito();
} }
Vantagens do uso da composição
Menor acoplamento, difícil de quebrar Quem faz e como faz esta escondido
Implementação pode ser trocada em runtime
Antes de usar herança
Estou fazendo por preguiça?
É uma relação de é um?
Não é um estado em que a classe pode ficar?
Não é um papel que a classe pode tomar?
Será que isso pode causar transtornos?
Object
Component
Container
JComponent
JPanel
JSpinner.DefaultEditor
JSpinner.DateEditor
Herança versus Interfaces
Programando voltado para interfaces
E se eu precisasse de polimorfismo?
Utilização de Interfaces
Reaproveitamento por composição
Classes abstratas na hora da preguiça!
Talvez package-friendly
Entity como interface
interface Entity { int getID();
} Entity
Usuario
class Usuario implements Entity { // ...
public int getID() { return this.id;
} }
Entity melhorada
Entity
Usuario EntityInfo
class Usuario implements Entity { private EntityInfo info;
public int getID() {
return this.info.getID();
} }
Temos de escrever o esqueleto sempre?
Entity
Usuario
EntityInfo Abstract
Entity
“Regras” do Gang of Four
1.
Prefira composição em vez de herança
2.
Programe pensando nas interfaces e não
na implementação
Leitura recomendada
Design Patterns, Gang of Four
Effective Java, Joshua Bloch
Refactoring, Martin Fowler
Obrigado! Perguntas e Respostas
Você pode ver esta palestra em:
http://www.paulo.com.br/