Boas práticas com Orientação a Objetos - IME-USPpeas/files/TutorialOOA.pdf · Herança, lados...

Post on 04-Jul-2020

0 views 0 download

Transcript of Boas práticas com Orientação a Objetos - IME-USPpeas/files/TutorialOOA.pdf · Herança, lados...

Boas práticas com Orientação a Objetos

Paulo Silveirapaulo.silveira@caelum.com.br

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/