Encapsulamento

47 respostas
_

Oi pessoal, bom dia.

Depois que o cv me instigou a ler mais sobre o assunto, encontrei o seguinte artigo, que deveria ter lido tempos atrás:

Nele, entre outras coisas, o autor afirma o seguinte:

Sendo assim, DAO não seria uma anti-pattern? Operações CRUD deveriam então ser realizadas dentro das próprias classes? Ou não?

E analisando a evolução do código do autor, percebi o uso de getters e setters pode ser arriscado, então deve-se pensar algumas vezes e escrever o código com cuidado.

Porém, ao usar frameworks como Hibernate, que provê muitas facilidades, o desenvolvedor é obrigado a definir getters/setters para todos os atributos a serem persistidos. E então? Usar ou não usar essas facilidades?

Obrigado pessoal.

47 Respostas

C

Colocar getters e setters pra satisfazer o framework nao te impede (bom, nao totalmente) de continuar com o encapsulamento bom - voce pode colocar as operacoes que lidam com aqueles dados na mesma classe mesmo assim.

Sobre o DAO, de certa forma eh um anti-pattern do jeito que normalmente se ve feito por ai. Uma alternativa seria o ActiveRecord, do tio Fowler: http://patternshare.org/default.aspx/Home.MF.ActiveRecord

PS: avatar comedia :mrgreen:

_

Beleza, entendi o lance dos getters/setters.

Mas cv, sobre o link que você me passou, bem … como implementar isso de forma coerente?

class Pessoa() implements HibernateSessionAware { // ... public void save() { session.save( this ); } }
Isso?

E como colocaria várias operações dentro de uma transação fazendo desta maneira? E se o meu domínio tem centenas de objetos, não fica meio complicado para manter essas classes que implementam ActiveRecord?

Obrigado cv :smiley:

ps.: fala pro sr Flower aumentar o tamanho do texto do site dele :mrgreen:

_

E mais uma dúvida: métodos que filtram os dados e retornam coleções do objeto em questão deveriam estar contidos na mesma classe? Se não, onde??

Lipe, que cada vez mais sabe que não sabe nada :smiley:

A

Voltando ao assunto do encapsulamento, outros artigos que causaram alguma polêmica foram estes do Allen Holub:

[list] Why getter and setter methods are evil [/list]
[list] Building user interfaces for object-oriented systems[/list]

Nesse último ele argumenta que MVC não é OO. Eu acho que ele se apega demais ao encapsulamento. É uma diretriz, não uma lei. De qq forma é uma boa leitura.

R

Eh preciso saber absorver o conteudo de muitos desses artigos. Alguem pode ler e tomar atitudes “drasticas” se confiar cegamente no que o autor diz, aumentando a “complexidade da aplicacao” (ou qualquer outra coisa, nao vem mto ao caso) por querer seguir a risca todas as “boas praticas” que leu pela net.
Fico imaginando a cena de alguem que acabou de ler o artigo onde eh argumentado que “mvc nao eh OO” e, por tal razao, declara a todos da equipe de desenvolvimento que “…nao vamos mais usar mvc pq ele nao eh oo”.

Rafael

A

De fato, Rafael, é arriscado se fiar na opinião de qualquer um que escreve um artigo. Todo mundo precisa saber pensar sobre o que lê.

O que eu achei interessante é o “absurdo” a que se chega quando se toma uma diretriz como se fosse um axioma absoluto. O projeto de um sistema envolve uma série de de escolhas de engenharia - se privilegia um aspecto em detrimento de outro. Isso talvez seja menos visível em software do que em outras áreas, porque as limitações físicas são muito menos importantes do que, por exemplo, ao se construir uma ponte.

O Holub acha que encapsulamento é uma regra inviolável. Mas, como os milhões de programas escritos usando MVC desde o smalltalk-80 mostram, é possível obter sistemas elegantes e funcionais relaxando um pouco o encapsulamento.

R

Um dos pontos que o artigo da javaworld nao fala sobre os getters eh o “problema da imutabilidade” das classes. O que alguns alegam eh que, tendo getters como temos a maior parte do tempo, as classes podem ter o status alterado facilmente. Imagine um codigo como

class TimerDoSistema {
    private Date dataInterna;

    public void init() {
        this.dataInterna = new Date();
    }

    public Date getData() {
        return this.dataInterna;
    }
}

digamos que o a intencao era nao permitir que a data interna fosse alterada, e portando nao foi adicionado um setXxx(). O problema eh que, por estar o getData() retornando a referencia para a instancia da classe Date, qualquer metodo podera ser chamado, mudando as prpriedades internas:

TimerDoSistema t = new TimerDoSistema();
t.init();

// Altera a data
t.getData().setTime(System.currentTimeMillis());

A solucao para isso retornar uma copia ao inves da referencia. Porem, fazer isso somente “por fazer” - ou seja, sem a necessidade real de imutabilidade - somente teria resultados mais “desfavoraveis” que a favor.

O problema, porem, eh fazer 90% dos desenvolvedores entender isso.

Rafael

A

Sei lá. Eu acho que imutabilidade na verdade deveria ser mais usada. Simplifica bastante o contrato da classe e vc não precisa se preocupar muito com mudança de hash-code. Além de poder dar alguma vantagem minuscula de performance (isso é secundário).

edição: Ficar copiando na hora de entregar para o cliente geralmente quer dizer q a classe do objeto retornado deveria ter sido imutavel. Acho q foi mancada da sun fazer o Date mutável.

L

DAO pode ser visto como um antipattern, mas também é solução pro caso de classes “Canivete Suiço”.

Eu acho que encapsulamento não pode ser tudo ou nada, tem que existir uma maior liberdade para outros objetos colaboradores olharem por traz da cortina.

Eu prefiro muito mais ter vários objetos colaborando entre sí que poucos objetos muito independentes. Essa é a velha discução de Coesão x Acoplamento e Spaghetti x Raviolli.

_

Adorei ler o artigo, ótimo ver uma opinião bastante diferente do que se ve hoje em dia.

Contudo, mandar meu objeto Candidato se gravar no reposítório de dados me cheira muito mal :expressionless:

C

Voce pode mandar seu objeto Candidato se gravar no banco de dados usando um CandidatoStore tal :wink:

P

O problema de getters/setters é que eles foram criados para um modelo de componentes gambiarral (ou vocês realmente acham que JavaBeans não são uma gambiarra? Convenção de nomes só apra reflection?Habla sério…) que foge das boas práticas e tende para o aparecimento de objetos sem comportamento, só atributos.

O uso de boas práticas ou não realmente é uma decisão muito séria, mas acho que deve-se conehcer o máximo possível para se saber quando usar e quando não usar.

Não me lembro de nenhum padrão J2EE que não tenha sentido apenas para suprir uma deficiência da plataforma, mas o DAO, IMHO, é mais que isso, age na impedância (cadê o link apra aquele outro tópico?). É ideal? Claro que não, mas funciona (Como DTOs, que até hoje eu busco algo que possa eliminar).

Um DAO deve ler o estado de um objeto e persistí-lo, ou as outras letrinhas do CRUD. Extrair o estado de um objeto apenas com consultas quebra o encapsulamento (a menos que houvessem “friends” em Java), mas uma solução para quem procura se manter nas boas práticas é aplicar o pattern Memento e persistir o Memento. Isso é meio trabalhoso e agrega uma complexidade que, na prática, só vai ser útil para se persistir o objeto.

F

Este? http://www.guj.com.br/posts/list/20496.java

]['s

_

cv, e nos casos de outras consultas que retornam coleções de objetos Candidato? Em que lugar enfio estes métodos sem doer?

Estou lendo o segundo artigo que o AllMighty postou, estou até achando perigoso de tanto que estou adorando o que o cara está escrevendo. Faz um monte de sentido pra mim :smiley:

C

O ActiveRecord (do Ruby On Rails, nao o Pattern) faz algo mais ou menos assim (traduzindo pra Java, e eu nao sei se acertei na sintaxe um-ponto-cincozenta):

public class Base<T extends Base> {

  public static T find(int id) { ... }
  public static Collection<T> find_by_example(T example) { ... }
  public static Collection<T> find_by_...

  public T save() { ... }
}


public class Candidato extends Base<Candidato> {
  private String nome;
  public void setNome(String nome) { ... }
  public String getNome() { ... }
}

dai, em algum outro lugar....

Collection<Candidato> foo = Candidato.find(1);
foo.setNome("Coelhinho da Pascoa");
foo.save();

Fica bonitinho, IMHO :mrgreen:

F

Olá,

Interessante esta abordagem. Recentemente montei uma arquitetura pra um sistema que to fazendo e tinha feito algo semelhante.
A parte do save, update, etc eu fiz deste jeito mesmo sem VO e direto no proprio Bean, usando uns commands pra isso. Algo tipo:

public class User extends Bean private String name; private String lastName; ... public void addUser() { this.session.save(this); }

Onde este atributo session é a implementacao de um interface que criei e tem somente os atributos save, update, delete, get e find. Somente ela reconhece o Dao.
Para a parte das consultas tava usando um ValueListHandle que era usado pelo meu Bean para realizar os find com as restricoes mais especificas. Nao ta gostando pq ia ter que ficar criando as classes do ValueListHandle dentro do bean e propagar a Session. Do jeito que o CV mostrou acho que resolvo meu problema com poucas alteracoes. :stuck_out_tongue:

O que acham desse modelo? :wink:

]['s

_

Poxa, chiquérrimo usando Generics. Ainda não me acostumei a pensar solução utilizando este recurso.

babando

Contudo, por que um objeto Candidato deveria ser responsável por buscar coleções de outros Candidatos? Por que ele deveria guardar informação sobre qual tipo de Candidato o usuário quer buscar?

E, nesse modelo, como sera possível alterar a classe Base para uma estrutura completamente diferente, baseado numa interface? Não seria melhor algo como

public class Candidato  {
   private Base&lt;Candidato&gt; base = factory.getBase();

   private String nome;
   public void setNome(String nome) { ... }
   public String getNome() { ... }
 }

sendo Base uma Interface?

cv, obrigadíssimo pelo exemplo, vou usá-lo :smiley:

C

O problema de fazer usando delegacao ao inves de heranca eh que vc tem que declarar os metodos estaticos :frowning:

Eu nao sei se rola usar generics em metodos estaticos, alias… se nao der, a ideia do ActiveRecord nao fica tao legal, mas mesmo assim da pra aproveitar. :smiley:

Por ultimo, concordo que usar um objeto Candidato pra buscar outros, mas usar a classe Candidato pra buscar fica chique - metodos estaticos bonitinhos e tal :mrgreen:

_

Ah gostei :smiley: fez sentido pra mim \o/ eeee

Obrigado mesmo cv :smiley:

Agora o problema grave é mandar esse mega bombado objeto pela rede hehe que tal usar DTO?
é esafqueaod por shoes

:mrgreen:

Procurei o livro do Martim Flower, POOEOoepP, mas não encontrei nada e não vou comprar ele agora :expressionless:

F

LIPE:
Agora o problema grave é mandar esse mega bombado objeto pela rede hehe que tal usar DTO?
é esafqueaod por shoes

Tudo bonitinho pra chegar no final e querer usar DTO.
Naoooooooooooo :mrgreen:

Fiquei so com uma duvida sobre o que o CV postou. Quando tu fala:

Era sobre o que eu tinha postado ou do LIPE?

:wink:

_

É sobre o próprio exemplo dele creio eu eheh

Quanto à sua implementação fabgp, quem fornecia o objeto session para os Objetos? E como você poderia colocar todos os comandos dentro de uma transação?

F

Ha, agora entendi.

Essa arquitetura eu usei em um sisteminha com WW, a Session ta sendo disponibilizada pela minha classe Transaction que é criada com um interceptor para as actions e ele seta na minha super-action a instancia dessa transaction.
Depois a session é passada para o bean quando vou executar algo neles.

Tem algumas coisas que nao gostei do modo que montei, mas ainda nao vi uma maneira eficiente de fazer isso. To pensando em como mudar ainda. :cry:

]['s

_

Ah, você provê o objeto session via IoC, isso?

Mas assim fica bem ruim para ter um controle melhor das transações envolvidas, não? Penso assim pois no sistema atual fiz algo semelhante, e quando precisei colocar ações em transações diferentes foi meio dolorido.

C

Precisa mesmo mandar ele pela rede? Hmm. Merda. Bom, pega o BeanUtils, taca tudo num HashMap, e passa pela rede. Depois recupera, e tchararaaaaaam! :mrgreen:

</solucao-porca>

Se o caso for mandar um objeto pesado pela rede e isso REALMENTE for um problema, DTO eh uma boa ideia. Mas, de novo: DTO so serve quando voce tem problemas em passar um objeto pela rede. Entao, antes de usar DTO, voce precisa provar pra si mesmo que o objeto eh grande demais e vai impactar a performance da aplicacao, e olhar pro codigo e falar “argh, ta grande” nao eh uma dessas maneiras.

Um jeitinho massa de saber se voce precisa fazer um DTO ou se vc pode continuar a viver sem a ajuda de aparelhos depois da visitinha do Shoes eh serializar o objeto que vc acha que eh “gordo” demais num arquivo, e ver o tamanho dele. Depois serializar uma quantidade significativa deles, e tirar uma media. Dai da pra ter uma ideia de quantos objetos vc vai conseguir passar pela rede sem melar tudo. :wink:

F

Isso.

Açoes tu diz, um save e um delete por exemplo na mesma Transacao?
Nao cheguei a precisar disso, mas confesso que nem tinha pensado nessa possibilidade. Se for isso é bom eu pensar e ver o impacto disso. hehehehe
Se for isso tu achou uma solucao sem doer muito? :mrgreen:

]['s

F

cv:
[
Um jeitinho massa de saber se voce precisa fazer um DTO ou se vc pode continuar a viver sem a ajuda de aparelhos depois da visitinha do Shoes eh serializar o objeto que vc acha que eh “gordo” demais num arquivo, e ver o tamanho dele. Depois serializar uma quantidade significativa deles, e tirar uma media. Dai da pra ter uma ideia de quantos objetos vc vai conseguir passar pela rede sem melar tudo. ;)

Estes tempos eu fiz um teste semelhante a este, achava que tinha uma colecao de objetos extremamente “gorda”, mas o java me desmentiu na lata. :lol:

]['s

_

O lance do DTO entendi :smiley:
fabgt, usei uma gambiarra. Nada bonito.

Mais uma dúvida, também em relação à tarefas normalmente realizadas por um DAO: e se uma consulta retorna 2 ou mais tipos de objetos completamente diferentes? Quem deve ser repsonsável pela coisa toda?

No caso de UIs que envolvam mais de um tipo de objeto, sem problemas. Uma classe fica responsável por montar a coisa toda. Seria o mesmo princípio aplicável para o problema acima? Criar uma classe gerenciadora para esses casos especiais? Mas isso não seria um DAO?

:?: :?: :?:

C

Acho que eu nunca vi uma consulta que retorna mais de um objeto diferente, e esses objetos nao tem relacao nenhuma… ja vi umas queries que retornam um monte de itens, de imagens a blocos de texto, mas todos os itens sao… errr, uhhm, filhos da classe Item :mrgreen:

_

Tem razão :smiley: não faz o menor sentido minha pergunta

C

ROFFLE LOLZOR BONUS POINTS A++++ WOULD DO BUSINESS AGAIN!!!111 pra essa camiseta do Spinal Tap :mrgreen:

L

Eu já, quer um bom exemplo?

Dada a seguinte query “me retorne os últimos registros que o usuario alterou”, ela não retorna os Rastros do usuario, mas sim os DO em questão.

_

O louds é foda :smiley:

Mas num caso desses o SGBD iria fazer um full scan em todas as tabelas @.@

Não seria uma solução mais apropriada fazer uma tabela de logging, muito bem representável por um objeto?

E considerando a situação que você apresentou, como seria adequado representar este comportamento dentro do sistema?

B

*Entrando na discussão…

Eu faria um log com os ids alterados. Depois recuperaria eles e iria em cada lugar pegar eles… :smiley:

Só pra não passar em branco, eu tb uso o mesmo modelo de Bean do Fabio : cada qual herda do Master o “script de CRUD” o qual retornado, eu pego uma SessionFactory e passo pra ela executar… meio doido, mas funciona :smiley:
Hnum, To usando tudo isso com Swing tah, e isso me deixa livre pra fazer uma interface pra apresentar o bean bunitinho na tela :smiley:

Mas to gostando da discussão…

_

brlima, aconselho a ler o segundo artigo indicado pelo AllMighty. O treco é bom :smiley:

Morte ao MVC!
facilmente convertido

:mrgreen:

F

LIPE:
Morte ao MVC!
facilmente convertido

Qual é o slogan agora? :mrgreen:

L

LIPE:
O louds é foda :smiley:

Mas num caso desses o SGBD iria fazer um full scan em todas as tabelas @.@

Não seria uma solução mais apropriada fazer uma tabela de logging, muito bem representável por um objeto?

E considerando a situação que você apresentou, como seria adequado representar este comportamento dentro do sistema?

Eu já implementei auditoria de 2 formas diferentes, cada qual tinha suas exigencias.

Uma foi usando shadow-tables, era bem facil gerar os dados de auditoria mas era uma saco usar essa informação. A outra foi usando uma tabela só com um campo clob com os valores em pares chave-valor, usa 1 tonelada de disco, mas fica mais facil de pesquisar e implementar usando interceptor do Hibernate foi assustadoramente facil.

Enfim, quanto a query, em ambos os casos os DAOs reconstruiam os DO a partir das informações recuperadas, no final das contas retornavam coleções de objetos heterogêneos mesmo. Existia um AutoriaDao que tinha esses métodos de pesquisa. Eu gosto de criar DAOs por casos de uso e não por DO que são partícipes.

E não tem full-table scan não lipe, vale lembrar que o modelo de dados e o modelo dos objetos de dominio não precisam ser parecidos ou ter coisas equivalentes, basta saber ir de um pro outro.

_

Então louds, o “objetivo” aqui seria não usar DAOs hehe mas sem um objeto intermediário aos dois Objetos não relacionados fica difícil.

F

Por isso eu nao gosto muito de mapear 1 pra 1, uma tabela pra um objeto, nem sempre isso faz sentido com a real necessidade do sistema. Esse exemplo que o Louds deu é um classico onde teu objeto nao tem nada haver com as tabelas do banco.
Acredito que o dominio do objetos deve ficar independete disso, satisfazendo a tua necessidade para as regras de negocio do sistema e o DAO que se vire pra retornar isso coerentemente. :wink:

]['s

L

Um bom exemplo disso é se você estiver trabalhando com OLAP ou com alguma forma de estatística, teu dominio muito provavelmente vai ter objetos que são agrupamentos de registros do banco.

A alternativa satisfatoria pro DAO, é ORM no estilo do Hibernate usando xdocklet ou annotations, uma pena apenas que ele te obriga a ter getters/setters.

Uma idéia que eu achei muito legal foi uma do Charles Miller de usar IoC para as pesquisas que o teu objeto precisa.

F

Tem algum artigo sobre louds?

]['s

L

fabgp2001, que tal o blog dele, de onde li sobre isso? :slight_smile:

http://fishbowl.pastiche.org/2004/04/04/cut_with_the_grain

A

E o que fazer com a coleção heterogênea de objetos? Se eles não implementarem ao menos uma interface em comum, o máximo de informação que podem dar é um toString()? Tem alguma mágica de reflexão?

L

QUANTO A TRANSAÇÕES… sei que é uma discução um tanto quanto antiga, mas achei o assunto legal e gostaria de compartilhar uma solução…

public ActionForward gravar(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    DynaActionForm atividadeForm = (DynaActionForm)form;
    Atividade      atividade     = null;
    
    SystemUserSession   systemUserSession   = new SystemUserSession(request);
    BusinessTransaction businessTransaction = getBusinessTransactionFactory().createNewBusinessTransaction((SystemUser)systemUserSession.getModel());
    
    try {
        businessTransaction.start();

        AtividadeBusiness atividadeBusiness = new AtividadeBusiness(businessTransaction);
        
        atividade = (Atividade)atividadeBusiness.newEntityInstance(
                ((Long)atividadeForm.get("idAtividade")).longValue()
        );
        BeanUtils.copyProperties(atividade, atividadeForm);

        try {
            businessTransaction.beginTransactionalBlock();

            atividadeBusiness.save(atividade);
            
            businessTransaction.commitTransactionalBlock();
        } catch (BusinessTransactionException bte) {
            businessTransaction.rollbackTransactionalBlock();

            setAlertMessage(request, be.getMessage());
        }           
    } finally {
        businessTransaction.finish();
    }
    
    atividadeForm.initialize(mapping);
    
    return mapping.findForward("_FormAtividade");        
}

Nessa solução que implementei na arquitetura que desenvolvi para fabrica de software que trabalho, criei uma classe responsavel por controlar o escopo (start/finish) e as ações (begin/commit/rollback) das trasações de negócio do sistema.

O fluxo é o seguinte…

  1. start() - cria uma sessão SessionFactory.
  2. Vários business-objects podem ser instanciados passando a instância “startada” de businessTransaction e manipulados.
  3. beginTransactionalBlock() - inicia uma transação do Hibernate (session.beginTransaction()).
    (save/remove/get/anyMethod).
  4. businessTransaction.commitTransactionalBlock() ou businessTransaction.rollbackTransactionalBlock() - comita ou descarta a trasação do Hibernate.
  5. businessTransaction.finish() - encerra a sessão do Hibernate.

Obviamente que existem outras classes e interfaces envolvidas na parada, mas o conceito é esse.

Da maneira como estruturei essa arquitetura, fica bastante fácil se amanhã ou depois eu não quiser mais utilizar o Hibernate e passar a usar um outro mecanismo de persistencia, porque toda a estrutura está baseada em interfaces.

Bom espero que ajude… espliquei bem por cima, como disse, tem várias classes e interfaces envolvidas, mas acho que deu para enternder o conceito, né?

Sei que ainda tem várias coisas para serem melhoradas e estou trabalhando todos os dias para fazer isso… :smiley: …sugestões são bem vindas…

Abraço!

L

AllMighty:
E o que fazer com a coleção heterogênea de objetos? Se eles não implementarem ao menos uma interface em comum, o máximo de informação que podem dar é um toString()? Tem alguma mágica de reflexão?

No meu caso eu tinha 1 factory de helpers que providenciavam a view e outras colaborações.

L

qual o jeito certo d fazer DAOs :? ?

B
cv:
O ActiveRecord (do Ruby On Rails, nao o Pattern) faz algo mais ou menos assim (traduzindo pra Java, e eu nao sei se acertei na sintaxe um-ponto-cincozenta):
public class Base<T extends Base> {

  public static T find(int id) { ... }
  public static Collection<T> find_by_example(T example) { ... }
  public static Collection<T> find_by_...

  public T save() { ... }
}

Em java num da pra usar generics em metodos Static! :(

D

Eu também já fiquei um pouco confuso com a questão dos objetos getters e setters. O que mais vejo são os programadores colocando os atributos como private e depois criando métodos set e get. A idéia do encapsulamento não seria esconder dos outros objetos, a forma como foi escolhida para representar um dado.

Por exemplo, se eu tenho uma classe chamada Turma que usa um ArrayList ou uma Hashtable para guardar os alunos:

private ArrayList alunos;

e criar um método getAlunos que retorna esse ArrayList, não seria a mesma coisa que deixar o atributo alunos como público?

Criado 23 de fevereiro de 2005
Ultima resposta 3 de mai. de 2008
Respostas 47
Participantes 12