boas práticas com orientação a objetos - ime-usppeas/files/tutorialooa.pdf · herança, lados...
TRANSCRIPT
Boas práticas com Orientação a Objetos
Paulo [email protected]
Rapidissímo overview
IFs e switches X herançaVantagens e desvantagens do uso de HerançaCasos errados do uso de herançaHerança versus ComposiçãoHerança versus InterfacesProgramando 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 todaaplicaçã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 Diretorextends 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 AdministradorO 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 formapublic 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 anomaliaPolimorfismo é muito mais eleganteVocê 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ódigoPossibilita 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.Propertiesjava.util.Stack
Properties
public class Properties extends Hashtable
Properties é uma Hashtable?
Hashtable: associa Object – Object
Properties: associa String - String
Properties não é uma Hashtable!
PropertiesObject
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 parecidoSe é 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 quebradosDifícil de mudar depoisPergunte 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 Entityclass 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 protectedclass 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 protectedclass 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ãeClasse 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
PessoaFísica
PessoaJurí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
PessoaFísica
PessoaJurídica
FornecedorJurídico
FornecedorFísico
Herança, lados negativos
Você ganha mais coisas do que gostariaSua relação com sua mãe é conturbadaQuebra 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
PessoaFísica
PessoaJurídica
FornecedorJurídico
FornecedorFí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 acomplamentoPessoa
PessoaFísica
FornecedorFísico
class FornecedorFisico extends PessoaFisica {}
passa para:class FornecedorFisico {
private PessoaFisica pessoa;
// metodos para delegar
}
Acabando com código duplicado
Pessoa
PessoaFísica
FornecedorFísico
PessoaJurídica
FornecedorJurídico
public String pegaNome() {
}public void enviaNotificacaoSobreCredito() {}
Acabando com código duplicado
Pessoa
PessoaFísica
FornecedorFísico
PessoaJurídica
FornecedorJurídico
FornecedorHelper
FornecedorHelperFaz o trabalho comum aos FornecedoresPode ser uma classe package-friendlyclass 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 quebrarQuem faz e como faz esta escondidoImplementaçã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 InterfacesReaproveitamento por composiçãoClasses abstratas na hora da preguiça! Talvez package-friendly
Entity como interfaceinterface 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
EntityInfoAbstract
Entity
“Regras” do Gang of Four
1. Prefira composição em vez de herança2. Programe pensando nas interfaces e não
na implementação
Leitura recomendada
Design Patterns, Gang of FourEffective Java, Joshua BlochRefactoring, Martin Fowler
Obrigado! Perguntas e Respostas
Você pode ver esta palestra em:
http://www.paulo.com.br/
Agradecimentos:
Nos vemos no BrasilOne!
http://www.brasilone.com.br/