Encapsulamento vantajoso ou porco?

Pra quem tem mais experiência aí, vcs consideram um erro quando em alguma parte do código vc tem uma chamada de métodos neste estilo:

//cor do tijolo da casa do prefeito
Color corTijolo = prefeito.getCidade().getBairro().getRua().getCasa().getParede().getTijolo().getCor();

Parece estar bem encapsulado, mas parece estar um tanto porco tb… sei lá… não sei outro modo interessante de fazer isso… O que me dizem?

isso não é um problema de encapsulamento, ne?

você poderia ter seus atributos sem encapsulamento e daria na mesma

Color corTijolo = prefeito.cidade.bairro.rua.casa.parede.tjolo.cor;

ok?

isso ai ta mais pra POG, será que vai dar um NullPointer ai?
qual deles está nulo?

há uma convesão (não vou achar referencia pra isso) que diz que quando você navega mais de 2 niveis há algo errado na sua modelagem ou
você deveria quebrar a linha fazendo algo como

rua = prefeito.getCidade().getBairro().getRua();
tijolo = rua.getCasa().getParede().getTijolo();
Color corTijolo = tijolo.getCor();

enfim, quando você faz uma coisa dessas você tem que no minio rever sua logica ou modelagem

Só reforçando a ideia: encapsulamento nunca é porco. Todo o objetivo de OO é encapsular.
Encapsular dados, comportamentos, dependências, algoritmos, decisões, etc…

 Color corTijolo = prefeito.getCidade().getBairro().getRua().getCasa().getParede().getTijolo().getCor(); 

Se você usasse uma linguagem que retornasse null quando você chamasse um método sobre um objeto null, até seria razoável. (Não sei se o Objective-C ou o Smalltalk são assim). Por exemplo, digamos que o prefeito não tenha casa; então getCasa() retorna null. Se você usasse uma linguagem dessas, então null.getParede() retornaria null em vez de provocar um null pointer exception, e null.getTijolo() retornaria null, e null.getCor() retornaria null, ou seja, a cor da casa do prefeito é null porque o prefeito não tem casa.

O problema é que se qualquer um desses métodos retornar null, você vai ter um null pointer exception medonho em vez de você ter uma resposta “null”, que é o razoável nesse caso.

Neste caso, você deveria criar um objeto especial para cada uma dessas classes (digamos CidadeNull, BairroNull, RuaNull etc.) para substituir o null. Por exemplo, RuaNull (um objeto da classe Rua, de preferência declarado como “static” para permitir comparação com “==” em vez de equals) poderia retornar para getCasa() o valor CasaNull. CasaNull pode retornar para getParede o valor ParedeNull. E assim por diante. É que nem sempre você pode controlar todas as classes. No seu caso, não podemos ter um Color que seja “null” porque ela é uma classe java.awt.Color; se ela não for final, podemos derivar uma classe Cor de java.awt.Color que tenha esse objeto especial CorNull.

Embora não esteja errado…ficou estranho…todos devem concordar com isso (eu acho… :D)

Mas se vc fizesse, APENAS COMO EXEMPLO:


public class Prefeito{

String nomePrefeito;
Casa casaPrefeito = new Casa();
}


public class Casa{

String nomeCasa;
String cor;
Cidade cidade= new Cidade();
}


public class Cidade{
String cidade;
}

nesse caso vc faria prefeito.getCasaPrefeito.getcor();

fica mais bonitinho né…

No caso então a melhor solução é simplesmente quebrar em mais linhas?

conforme o ddduran:
rua = prefeito.getCidade().getBairro().getRua();
tijolo = rua.getCasa().getParede().getTijolo();
Color corTijolo = tijolo.getCor();

Ou, facilitando a manutenção, apesar de aumentar o código:
cidade = prefeito.getCidade();
bairro = cidade.getBairro();
rua = bairro.getRua();
casa = rua.getCasa();
parede = casa.getParede();
tijolo = parede.getTijolo();
Color corTijolo = tijolo.getCor();

Talvez ficaria melhor essa segunda opção, escrita dentro de um método ‘private getCorTijolo()’ colocado na classe onde estou escrevendo isso tudo, correto?

Não sei se a solução é “quebrar em mais linhas”. Acho que você poderia usar o “pattern” do “Null Object” que lhe expliquei para poder contornar esses problemas chatos do tipo “como o prefeito não tem casa, então eu tenho um null pointer exception, em vez de saber que a cor é null porque o prefeito não tem casa”.

[quote=wellington7]Pra quem tem mais experiência aí, vcs consideram um erro quando em alguma parte do código vc tem uma chamada de métodos neste estilo:

//cor do tijolo da casa do prefeito
Color corTijolo = prefeito.getCidade().getBairro().getRua().getCasa().getParede().getTijolo().getCor();

Parece estar bem encapsulado, mas parece estar um tanto porco tb… sei lá… não sei outro modo interessante de fazer isso… O que me dizem?[/quote]

Quando eu olho algo desse tipo eu lembro de xpath

/cidade/bairro/rua/casa/parede/tijolo/cor

ou, se der pra simplificar

//tijolo/cor

Agora… essa ‘montoeira’ de métodos poderia funcionar sem null pointer exception se vc garantir que nenhum método retorna null. Se o prefeito não pudesse e não tivesse uma casa, ele poderia lançar uma ‘CasaNotFoundException’.

Nao acho que seria porco mas poderia dar uma reduzida, nao que esteja errado somente poderia ser menor se parar para pensar…mas nao esta errado nao…

// Só para simplificar, estou supondo que cada classe tem apenas um atributo de cada tipo.
// Obviamente uma casa tem mais paredes e uma parede tem mais tijolos :P
class Tijolo {
    private Color cor;
    public Tijolo (Color cor) { this.cor = cor; }
    public Color getCor() { return cor; }
    public static final Tijolo Null = new Tijolo (null); 
}
class Parede {
    private Tijolo tijolo;
    public Parede (Tijolo tijolo) { this.tijolo = tijolo; }
    public Tijolo getTijolo() { return tijolo; }
    public static final Parede Null = new Parede (Tijolo.Null);
}
class Casa {
    private Parede parede;
    public Casa (Parede parede) { this.parede = parede; }
    public Parede getParede() { return parede; }
    public static final Casa Null = new Casa (Parede.Null);
}
class Rua {
    private Casa casa;
    public Rua (Casa casa) { this.casa = casa; }
    public Parede getCasa() { return casa; }
    public static final Rua Null = new Rua (Casa.Null);
}
class CorDaCasaDoPrefeito {
    public static void main(String[] args) {
        ....
        // imprime "null" em vez de lançar um NullPointerException.
        System.out.println (prefeito.getCidade().getBairro().getRua().getCasa().getParede().getTijolo().getCor());
    }
}

ptz cara, sinceramente esse negocio do null não me agrada muito, você ja usou isso?

acho que o negocio é quando você tiver a necessidade de navegar tanto entre seus objetos é rever sua modelagem ou fazer alguma fabrica

[quote=wellington7]Pra quem tem mais experiência aí, vcs consideram um erro quando em alguma parte do código vc tem uma chamada de métodos neste estilo:

//cor do tijolo da casa do prefeito
Color corTijolo = prefeito.getCidade().getBairro().getRua().getCasa().getParede().getTijolo().getCor();

Parece estar bem encapsulado, mas parece estar um tanto porco tb… sei lá… não sei outro modo interessante de fazer isso… O que me dizem?

[/quote]

Na minha opinião, melhor fazer prefeito.getCorTijolo(), e por dentro desse método, você faz o aninhamento de métodos necessários (e cria exceções apropriadas caso algum erro ocorra). A razão disso é que você oculta o esquema navegacional para o cliente da interface, pois a navegação pode mudar com o tempo.

Além disso, o exemplo não é muito adequado. Imagine que um objeto Cidade seja retornado chamando prefeito.getCidade(): ao invocar getBairro() nesse objeto, quantos objetos Bairro deveriam ser retornados? Vários, pois uma cidade é um conjunto de vários bairros, e aí o restante dos métodos estariam inválidos pois não posso invocá-los em uma Collection ou em um Array. Se for pra retornar um, significa que o objeto Cidade conhece o objeto Prefeito, e aí a acoplamento está perigosamente alto.

Pense nisso.

Existem duas possibilidades. A primeira é a que você está usando Fluent Interface: http://martinfowler.com/bliki/FluentInterface.html

A segunda é que você está querendo fazer um código OO normal em Java, neste caso está quebrando a Lei de Deméter: http://en.wikipedia.org/wiki/Law_of_Demeter

E é sempre bom lembrar: get/set e encapsulamento têm muito pouco a ver um com o outro.

[quote=pcalcado]Existem duas possibilidades. A primeira é a que você está usando Fluent Interface: http://martinfowler.com/bliki/FluentInterface.html

A segunda é que você está querendo fazer um código OO normal em Java, neste caso está quebrando a Lei de Deméter: http://en.wikipedia.org/wiki/Law_of_Demeter

E é sempre bom lembrar: get/set e encapsulamento têm muito pouco a ver um com o outro.[/quote]

Segundo a Lei de Demeter acho que a minha idéia foi a mais próxima…onde casa tem coisas de casa e prefeito tem coisas de prefeito…se precisar de alguma coisa sobre a casa do prefeito…vc acessa através da instância casa dentro do prefeito (prefeito TEM-UM casa)

[quote=pcalcado]… A segunda é que você está querendo fazer um código OO normal em Java, neste caso está quebrando a Lei de Deméter: http://en.wikipedia.org/wiki/Law_of_Demeter
[/quote]

Neste caso, recomendo a leitura deste post.

Ótimo post theMask.
Eu já conhecia a law of demmeter mas o exemplo que o cara deu foi muito bom.
Recomendo.

[]s
Ferry

Muito interessante isso!

Mas com relação a este código:

private void makeNormal(Customer customer) {
        Order o1 = new Order();
        customer.addOrder(o1);
        OrderLine line1 = new OrderLine(6, Product.find("TAL"));
        o1.addLine(line1);
        OrderLine line2 = new OrderLine(5, Product.find("HPK"));
        o1.addLine(line2);
        OrderLine line3 = new OrderLine(3, Product.find("LGV"));
        o1.addLine(line3);
        line2.setSkippable(true);
        o1.setRush(true);
    }

Se você começa a repetir muito isso:

OrderLine line1 = new OrderLine(6, Product.find(“TAL”));
o1.addLine(line1);

Não seria um mau cheiro? Neste caso acho que este trecho deveria ser extraído para um novo método e depois mover o novo método para dentro de Order. No final ficaria assim:

private void makeNormal(Customer customer) {
        Order o1 = new Order();
        customer.addOrder(o1);
        o1.addLine(6, "TAL");
        o1.addLine(5, "HPK").setSkippable(true); //addLine passa a retornar o OrderLine adicionado
        o1.addLine(3, "LGV");
        o1.setRush(true);
    }

Aplicando novas refatorações chegariamos bem próximo a uma interface fluente.

Outra dúvida:

private void makeFluent(Customer customer) {
        customer.newOrder()
                .with(6, "TAL")
                .with(5, "HPK").skippable()
                .with(3, "LGV")
                .priorityRush();
    }

Quando ele muda isso: .setSkippable(true) e .setRush(true) para .skippable() e .priorityRush(). Elenão estaria adicionando a necessidade de aplicar uma refatoração (que eu esqueci o nome :oops: ) para voltar para .setSkippable(true) e .setRush(true)

Ou consultar o livro do Larman 2 ED pg 346 obs: na edição em português ta como: Lei de Demétrio!

Para os extremistas ressalto isso: Fowler começa falando sobre como a prática de evitar getters a todo custo confunde o sentido real de encapsulamento. Tem horas que gets e sets te ajudam a manter o encapsulamento!

Não necessariamente. Utilizar uma Fluent Interface é quebrar o estilo da linguagem então não dá apra aplicar simplesmente os princípios de deisng em Java. Ao fazer a alteração acima você está partindo de uma Minimal Interface para uma Humane Interface, o que pode ser o que você precisa no caso ou não, por isso que falei que ou é uma coisa oa (Fluent) ou uma ruim (demeter) no exemplo do autori original do post.

[quote=pcalcado][quote=brunohansen]
Quando ele muda isso: .setSkippable(true) e .setRush(true) para .skippable() e .priorityRush(). Elenão estaria adicionando a necessidade de aplicar uma refatoração (que eu esqueci o nome :oops: ) para voltar para .setSkippable(true) e .setRush(true)
[/quote]

Não necessariamente. Utilizar uma Fluent Interface é quebrar o estilo da linguagem então não dá apra aplicar simplesmente os princípios de deisng em Java. Ao fazer a alteração acima você está partindo de uma Minimal Interface para uma Humane Interface, o que pode ser o que você precisa no caso ou não, por isso que falei que ou é uma coisa oa (Fluent) ou uma ruim (demeter) no exemplo do autori original do post.[/quote]

Vou dar uma lida lá.

Ainda descubro como vc e o Luca arrumam tanto tempo para ler e escrever! O dia de vcs tem quantas horas? rs…

Eu sei que foi para exemplificar,

mas, tanto nesse caso como na maioria dos outros, existem equivocos de modelagem ou de relação entre os objetos, afinal para saber a cor da casa do prefeito, seria mais lógico obter a casa do prefeito e depois a cor dela.

Isso faz com que a métrica de termos no máximo dois níveis se torna algo mais factivel.

Pensando um pouco de “forma estruturada”, vocês já viram uma relação de NxN entre duas entidades que precise mais que uma tabela de ligação, em um caso real? Logo esse modelo é um exemplo de algo que “não ocorre no mundo real” e dificilmente irá ocorrer na modelagem OO que tenta abstrair do mundo real as relações entre os objetos.

pelo menos é a minha opnião.

fw