| Autor |
Mensagem |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 01/09/2008 08:48:30
|
g4j
GUJ Ranger
![[Avatar]](/images/avatar/bea3c20edb84a0dd83a99a9a7274bc67.jpg)
Membro desde: 02/05/2007 14:32:03
Mensagens: 816
Localização: Curitiba
Offline
|
Eu estava escrevendo, "quotando" tudo, mas desisti.
Pelo que eu entendi, a discussão é pelo simples fato de classes terem todos os comportamentos definido em outras classes, o que muitos chamam de BO, ficando assim somente com os getters/setters.
Mas algumas classes têm somente métodos CRUD. Esses métodos devem estar nas classes?
Ou então aqui os DAOs entram? Ops, a classe User vai ficar "anêmica".
This message was edited 1 time. Last update was at 01/09/2008 08:49:16
|
Gerson Luiz Chagas
SCJP 5.0
SCWCD 5.0
|
|
|
 |
|
|
![[Post New]](/templates/default/images/icon_minipost_new.gif) 01/09/2008 09:16:43
|
pcalcado
Moderador
![[Avatar]](/images/avatar/110eec23201d80e40d0c4a48954e2ff5.jpg)
Membro desde: 08/03/2004 17:19:35
Mensagens: 5174
Localização: Sydney - Australia
Offline
|
g4j wrote:Ou então aqui os DAOs entram? Ops, a classe User vai ficar "anêmica".
Não, creio que você não tenha entendido o princípio.
Um objeto é responsável por seu comportamento. Um objeto não se armazena em si mesmo, você o armazena em algum lugar, daí já surge a necessidade de outro objeto com esta responsabilidade. O ponto é que comportamento ("métodos") e estado ("atributos") relativos a um objeto devem estar nele e não em 2 ou mais classes.
|
Phillip Calçado "Shoes"
http://fragmental.tw/
http://blog.fragmental.com.br/
"It is unfortunate that much of what is called 'object-oriented programming today is simply old style programming with fancier constructs." - Alan Kay |
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 01/09/2008 09:34:01
|
victorwss
JWizard
![[Avatar]](/images/avatar/4ab232445f9b21b65dfdf6ea5f27f704.png)
Membro desde: 18/12/2007 14:46:00
Mensagens: 2409
Localização: São Paulo - SP
Offline
|
sergiotaborda wrote:
victorwss wrote:
Primeiro erro:
VOs (no sentido anêmico) são apenas um monte de getters e setters burros que apenas tornam os atributos públicos com um falso encapsulamento. Estes atributos se limitam a apenas refletir o BD.
Certo. O que vc tlv não tenha percebido é o papel dos get/set. Eles não servem para encapsular ( embora encapsulem), servem para criar - em java - o conceito de propriedade.
Os VO no sentido que está usando a palavra são objetos apenas com propriedades.
Se vc incluir algum método que não é uma propriedade em qualquer objeto java ele não deve ter o nome prefixado com get/set. O seu exemplo do diametro. Diametro não é uma propriedade, é calculado.
Logo não faz sentido ter um getDiametro e um setDiamentro. O que faz sentido é ter um set/getRaio - que é a propriedade e ter um diametro() que é um método que calcula o diametro. Esse método não é uma propriedade, ele contêm inteligência e portanto torno o seu objeto em algo não-anémico.
É uma questão de seguir o padrão Bean (Não confundir com JavaBean) que dita que as propriedades têm que ser marcadas com get/set.
O problema é que muito pouco entendem que em java se declaram propriedades dessa forma e isso causa um monte de problemas.
Ok, vamos supor o seguinte: No banco de dados existe um campo raio. O que significa que o FP usará o atributo raio ou os respectivos getters e setters. Mas, em todo o sistema se trabalha com o diâmetro. Sim, isso é uma inconsistência, mas vamos supor que seja porque o banco de dados é legado ou pertence a terceiros, por exemplo. Pois bem, como o sistema trabalha com diâmetro mas persiste o raio, eu poderia colocar um par de getters e setters para o diâmetro, centralizando a regra de negócio (conversão de raio para diâmetro e vice-versa) no objeto. Mas, se eu não criasse esses getters e setters, o que aconteceria? Eu seria obrigado a pulverizar a regra de conversão em todo o sistema ou então criar algum outro objeto apenas para fazê-la, quando seria muito mais simples, mais fácil E MAIS OO se o próprio objeto soubesse fazê-la.
sergiotaborda wrote:
É verdade que os VO normalmente espelham uma tabela qualquer, mas não precisa ser assim. Isso é falha do modelador, não do padrão , nem da tecnologia. Se a tabela tem 100 campos - como exemplo que o saoj deu - e o cara modela isso como um objeto com 100 campos o que podemos concluir é que ele não sabe - ou não quer - modelar OO. Parta os 100 campos em pedaços que sejam relevantes juntos e crie um objeto com esses objetos como propriedades. Não precisa usar esse objeto se não quiser, pode usar apenas os pedaços por si mesmos (este é o conceito moderno de VO). Continuamos apenas tendo propriedades, mas em nada estamos amarrados ao banco.
Sim, a falha nasce aí. Programação orientada ao BD. Foi por isso que eu chamei isso de "Primeiro erro". É aí que o problema nasce e passa a governar todo o sistema.
sergiotaborda wrote:
Normalmente o BD é criado primeiro (mesmo porque sem ele, o projeto quase não sai do lugar).
Infelizmente a maioria ainda pensa assim. Em OO o que vem primeiro é o modelo de classes que são o coração do programa - o dominio. O testo é plumbing. Modelar o sistema em prol do banco é simplesmente coisa do século passado ( literalmente) e não é OO.
Sim, isso é parte do "Primeiro erro". E na maior parte dos casos isso se resolve se o BD refletir o sistema e não o contrário (como o pcalcado disse). O problema é quando o BD é legado ou pertence a terceiros, não se pode deixar que um "desejo profano" do BD interferfira na estrutura do seu sistema.
sergiotaborda wrote:
Segundo erro:
Me tomando como exemplo, estou dando manutenção em um projeto que usa um framework de persistência que é horrível. Basicamente ele faz CRUD em apenas uma única tabela por vez (ou seja, nunca gera SQLs que contenham joins).
(...) e o framework fosse setar o diametro da sua classe, vai ocorrer um belo NoSuchFieldException, uma vez que ele não acha o atributo raio. Isso força você a espelhar a estrutura do BD.
Não.
Não conheço o framework de que fala, mas se bem entendi ele utiliza os métodos get/set para ler/escrever os campos. Isso é mais que correto. O framework não pode ler diretamente via reflection direta do atributo provado. Isso é gambiarra. É privado, é suposto ninguem saber que ele existe.
Então, você não entendeu. O framework em questão (que é um completo lixo por sinal) SEMPRE acessa o atributo diretamente e não quer nem saber se existe getter ou setter. Estou dando manutenção no sistema e tentando refatorá-lo para eliminar esse framework para sempre.
sergiotaborda wrote:
Muitos frameworks leem direto dos campos, mas isso é falho. Experimente usá-los em ambiente seguro e terá uma grande surpresa. O erro é do cara que modelou o diâmetro como uma propriedade quando claramente não é. Até UML tem um símbolo especial para campos desse tipo ("calculados"  . Não é nada do outro mundo. E a convenção é muito simples. Não ha modificador e o acesso não começa com get.
Neste caso, você está afirmando que se toda a minha lógica de negócio for em cima do diâmetro, mas por algum motivo qualquer o BD trabalhe com o raio e isso não possa ser mudado, então eu vou ser obrigado a mudar toda a lógica do meu sistema apenas para satisfazer um desejo do BD porque o raio deveria ser uma propriedade e não o diâmetro?
sergiotaborda wrote:
Terceiro erro:
Ok, joguemos no lixo os frameworks que trabalham com atributos e troquemos por frameworks que usam getters e setters. Essa abordagem força você a colocar getters e setters em tudo, o que provavelmente torna a maior parte dos campos como se fossem públicos e se não fizer isso, o framework explodirá com um belo NoSuchMethodException.
O que é corretíssimo.
Não ha nenhum problema em colocar get/set porque isso lhe permite definir outras coisas que não são propriedades. Os frameworks de persistencia só se interessam pelas propriedades então se o objeto está bem modelado, não vai haver problema nenhum.
É correto eu compremeter o encapsulamento do meu objeto só porque o FP precisa disso?
sergiotaborda wrote:
Dificilmente poderá ser feita alguma validação no setter, pois se alguma exceção for lançada, o framework provavelmente não saberá tratá-la. O máximo que você pode fazer é falhar silenciosamente ou setar algum outro valor diferente daquele do parâmetro do setter.
Validação no setter... ora ai está outro mito... o setter faz no máximo uma consistência. Validação e consistência são duas coisas diferentes. Mas veja bem, o framework de persistência (FP)só trabalha com dados que já foram incluídos no objeto. Se quando eles foram incluídos tudo estava bem, isso irá ser sempre assim.O FP não altera os dados, ele só copia. Se estava certo continuará certo.
Mas se as regras do objeto mudaram, ou alguem mexer no banco À mão é bom que haja erros de consistência e até de validação.
Mas diga-me : quando vc lê dados do banco - à mão ou com frameworks - vc passa-os pelo mesmo validador de quando eles vêm da UI ? Passa-os por algum validador ? Se não, é porque vc confia bastante no banco. Se vc confia, porque o FP não pode confiar ?
Insira as consistências à vontade porque sempre lhe serão uteis. Se der pau na hora de carregar do banco é porque o banco mudou ou as regras mudaram, em qualquer caso é bom ser avisado.
Vamos supor que eu NÃO CONFIE no BD. Vamos supor que o número de parcelas deve sempre ser positivo, mas vamos dizer também que no passado havia algum bug que fazia com que em alguns registros surgisse um número de parcelas negativo. Se no meu setter eu colocar um if (numeroParcelas <= 0) throw new IllegalArgumentException(), qual é o resultado? O FP explode. Até que está correto em ele explodir, pois está trabalhando com algo inválido. O problema é que isso por sua vez pode comprometer a robustez e deixar o sistema extremamente intolerante a falhas (inclusive impedindo que o usuário possa retificar o valor sem acessar o BD diretamente). Isso força eu simplesmente deixar que o objeto fique inválido ou então fazer o setter falhar silenciosamente ou setar algum campo para avisar que o objeto é defeituoso ou qualquer outro tipo de gambiarra apenas para remendar o problema.
sergiotaborda wrote:
Quarto erro:
Os frameworks quase sempre criam os seus objetos de uma única forma: Através do construtor padrão (e normalmente público).
Se assim não fosse eles estariam a) violando o encapsulamento ou b) inferindo regras de construção do dominio. FP não podem fazer nenhuma dessas duas coisas. Sim, é claro que poderiam usar um factory ou um método estático como mais um arsenal mas... na real, qual é o programador que quer fazer isso ? A maioria nem sabe declarar o construtor direito ...
Concordo com quase tudo disso, especialmente se o framework pudesse usar uma factory seria muito legal. Só discordo no ponto de "na real, qual é o programador que quer fazer isso ? A maioria nem sabe declarar o construtor direito ... " Se eu estiver criando um objeto Semáforo que já venha em um estado "Verde, amarelo ou vermelho", sou obrigado a criá-lo sem estado para depois dar um setCor nele? Isso cria um problema de coesão sequencial. E se a maior parte dos programadores não sabe declarar construtor, bem, acho que não preciso comentar isso.
sergiotaborda wrote:
Esse construtor, por obviamente não receber parâmetros, é obrigado a setar valores padrão nos atributos ou então deixá-los todos nulos. Em seguida o framework invoca os setters em uma ordem não-conhecida para popular o objeto, o que torna difícil controlar a consistência dele.
Vê? ai está o erro. A consistência é algo unitário e inerente ao modificador. O modificador sabe, sozinho,
consistir os dados. Se ele não sabe, isso não é consistência, é validação ( que é feita por um outro objeto: o validador). Por outro lado, pensar que ha uma ordem para invocar os set (escrever as propriedades) é absurdo, porque o próprio conceito de propriedade significa que elas são independentes. Se não são, algo está errado na modelagem do objeto, não no FP.
Ou seja, na verdade eu seria obrigado a colocar a validação em um outro objeto, quando na verdade ela deveria estar no objeto em questão. Mas, isso mostra uma direção a seguir para resolver o problema. Se o VO fosse um objeto que simplesmente fosse usado para isolar o BD e mais nada, o objeto de verdade teria a validação dentro dele mesmo.
Sim, o conceito de propriedades diz que elas devem ser independentes, mas nem sempre isso é possível porque as regras de negócio podem exigir coisas estranhas. E quando isso acontece, fazer o que? Sentar e chorar? Socar gambiarra?
sergiotaborda wrote:
Pior ainda no caso de acesso direto aos atributos: não há forma de evitar que o objeto entre em estado inválido. Também não é possível lançar exceções de dentro do construtor e nem fazer qualquer tipo de delegação. O framework também nunca avisará que o objeto está pronto e que nenhum outro atributo ou setar será usado, o que impossibilita fazer a verificação de consistência após o framework fazer o trabalho.
... gritaria estérica isso ai... o fp vai dar um new no objeto e escrever as propriedades. Isso é feito transacionalmente, se der algum problema em algum dos passos o objeto simplesmente não é criado.
O objeto pode até entrar em estado inconsistente ( pensemos que sim, para continuar o argumento) mas o FP nunca lhe retornará esse objeto. Ele vai dar exception antes.
Portanto, mesmo que ele entre em estado inconsistente, isso não será problema.
( Supondo que o FP está construido corretamente. Se não está é um problema do framework, não do padrão VO ou do padrão Bean)
Errado, o problema é exatamente que NÃO vai ocorrer nenhum problema e NÃO vai dar nenhuma exception. Para o FP é como se esses atributos fossem públicos e ele fará o que bem entender com eles e ninguém conseguirá impedi-lo. O máximo que você poderá fazer é verificar se o que o FP produziu faz sentido, mas não há nada que possa impedi-lo de criar e retornar tal objeto.
sergiotaborda wrote:
Quinto erro:
Como toda a estrutura da aplicação está nas VOs e estas tem que ser burras porque o framework de persistência é burro, a lógica da aplicação acaba tendo que ir para outro lugar, aí é que surge a BO. Mas, o estado da aplicação está todos nos VOs porque o framework os criou assim. Acaba parecendo ser inútil (**) replicar esse estado nos BOs, o que de fato mantém o estado dos objetos separado das operações que estes realizam.
Vc assume que o problema é o FP quando na realidade é a modelagem do VO. Se for bem feita o FP não vai interferir. É tudo uma questão de convenção. A lógica não tem que ir para outro lugar - exemplo do diametro(). O ponto é o que fazer com lógica que está acima dos objetos de dados. lógicas que não dependem do estado dos objetos, mas das relações dos seus estados. Isso é colocado em outros objetos - sem estado - que fazem algo. Objetos sem estado são os melhores que ha porque poupam recursos.
Por outro lado, nem todas as lógicas cabem em VO não-anémicos ( Por isso o DDD fala em Services).
Em um mundo perfeito (o modelador e o DBA são a mesma pessoa) sim, dá para dizer que esse problema acabaria sendo causa de erro da modelagem. Mas, deixa eu ver se entendi, você acha que separar o estado do objeto da sua lógica é algo bom?
sergiotaborda wrote:
Sexto erro:
Nas camadas acima da camada de negócio (camadas de apresentação, normalmente). Frequentemente é necessário acessar o estado dos objetos. Como as BOs são stateless e a quantidade de acesso a propriedades é grande, as VOs acabam sendo passadas a elas diretamente.
Reuso é a razão para fazer isso. O problema é que algumas tecnologias exigem get/set para tudo e mais alguma coisa e não sabem trabalhar com o diametro() apenas. Isso leva o programador a criar um getDiametro. Mas isso está em desacordo com as regras dos Beans e portanto com as dos FP que seguem as regras dos Beans. É bom que todos os frameworks em todas as camadas cumpram as mesmas regras. É o fato disso não ser verdade que leva a absurdos como getDiametro().
Pois é, leva a isso (ex: Expression Language). Mas, vai fazer o que? O problema na verdade é que definir propriedades como um par de métodos com o prefixo "get" e "set" é a maior gambiarra existente na linguagem java. Neste ponto VB e C# são mais felizes. Quem dera se annotations tivessem existido desde o começo... É por isso que considero essa coisa de (java)beans como um antipadrão, uma vez que se baseia na má idéia de inferir a estrutura dos objetos baseando-se no nome dos métodos. Se annotations tivessem surgido no começo, nada disso teria acontecido.
sergiotaborda wrote:
A situação fica ainda pior quando a camada de apresentação e de negócio estão em JVMs distintas, pois nesse caso o padrão TO é recomendado. O TOs normalmente é (e deve) ser modelado como um objeto com getters burros (o que implica atributos praticamente refletindo os getters). Mas por questão de reusabilidade, uma vez que o VO nada mais é do que o TO com os setters a mais, o VO acaba assumindo o lugar do TO.
Essa ai nem existe. Se o seu VO é Serializable ( o que não é dificil) ele pode ser usando inter-JVM sem problemas. O problema ocorre quando vc expoe uma interface publica com esses objetos como num webservice, por exemplo. Ai vc precisa da conversão para isolar a camada de apresentação ( webservice) do sistema em si. Mas fora isso , não ha problema nenhum em usar VO Serializáveis no lugar de TO ...
Por si só isso não é um problema. O problema é que isso acaba criando a falsa idéia de que o uso das VOs é legítimo e dificulta o trabalho de eliminá-las, simplesmente porque isso faz com que elas aparentemente simplesmente não possam ser eliminadas. Uma possível saída seria utilizar Map<String, Object> no lugar, mas isso com quase certeza vai acabar criando problemas ainda piores.
sergiotaborda wrote:
Para resolver o problema e fugir da arquitetura bolo de ovo, é necessário atacar a sua causa. Qual é a principal causa? frameworks de persistência ruins, que forçam a aplicação a ser modelada de uma forma relacional, e não OO.
Não. 'Modeladores' ruins, que sabem a diferença entre getDiametro() e diametro(). 'Desenvolvedores' ruins que não escolhem os seus frameworks de forma compatível e não conhecem os padrões direito acabando por usar o que está na moda sem pensar nas conseqüências.
E você se esqueceu do principal: Ter bases de dados e/ou aplicações legadas ou de terceiros. E também frameworks que não entendem diametro() e exigem getDiametro() (que como você sabe, remete aos JavaBeans). Outra coisa é o setDiametro. Você usaria setRaio(valor * 2) em TODOS os lugares que precisassem setar o diâmetro?
sergiotaborda wrote:
Ou seja, a primeira coisa a fazer é escolher um framework decente (ou então usar a JDBC na unha). Isso elimina de cara o segundo, o terceiro e o quarto erro, o que impede que o quinto e o sexto ocorram.
A primeira coisa a fazer é aprender a programar direito e entender os padrões. Com isso elimina toda essa treta de anti-padrão de dominio anémico. Veja que todo o problema que vc levantou é baseado num simples erro de modelagem por não entender a diferença entre modificador, acessor e métodos simples , junta com a má escolha de frameworks nas várias camadas. Não ha nenhum bicho papão de bolos de ovo, aliás todos os bolos são de ovo, não dá para fazer bolos sem eles (... até dá, mas na culinária, não na expressão "fazer bolo sem quebrar ovos"...  )
Ok, pode-se até quebrar os ovos. O negócio é não fazer meleca!
sergiotaborda wrote:
Não são os frameworks que são burros. São os seus idealizadores e o seus utilizadores que são ineptos.
É claro que inépcia é uma coisa passageira, basta que as pessoas estudem mais. Algumas outras doenças mais graves e não são tão passageiras... ( como a preguiça , por exemplo...)
Eu não disse que todos os frameworks são burros. Eu disse que há muitos frameworks burros (e isso você tem que concordar).
|
Victor Williams Stafusa da Silva
Bacharel em Ciência da Computação - UFMT // Especialista em Desenvolvimento Java - CEFET/MT // Doutorando em Ciência da Computação - IME-USP
SCJP 6.0 - 19/12/2007 - PASS - 88% // SCWCD 5 - 17/05/2008 - PASS - 79% // SCJA - 09/09/2008 - PASS - 96% // SCSNI - 30/06/2009 - PASS - 68% // SCBCD 5 - 31/05/2010 - PASS - 95%
Próximos: SCJD (encalhado com o projeto), SCEA parte I (estudando). Algum dia desses: SCMAD, OCA, SCEA e SCDJWS.
Computação: uma ciência holística e esotérica!
E então veio Deus a terra e disse aos homens: Não dividireis por zero.
XML is a giant step in no direction at all. (Erik Naggum)
Arquitetura de sistemas: Eu prefiro ser essa metamorfose ambulante do que ter aquela velha opinião formada sobre tudo.
Diga não as drogas: Não use java.util.Vector.
Cuidado: Este usuário pode ter temperamento agressivo.
Always code as if the person who will maintain your code is a maniac serial killer that knows where you live.
I am the maniac serial killer that knows where you live who will maintain your code.
É impossível falar de CMMI (Capability Maturity Model Integration) sem saber o que é CIMM (Capability Im-Maturity Model).
Se você escreve "concerteza", "concerteza" você andou matando aulas de português. |
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 01/09/2008 12:10:36
|
sergiotaborda
GUJ Expert
![[Avatar]](/images/avatar/b4a0e0fbaa9f16d8947c49f4e610b549.png)
Membro desde: 22/03/2005 20:57:48
Mensagens: 3433
Offline
|
victorwss wrote:
sergiotaborda wrote:
(...)
Logo não faz sentido ter um getDiametro e um setDiamentro. O que faz sentido é ter um set/getRaio - que é a propriedade e ter um diametro() que é um método que calcula o diametro. Esse método não é uma propriedade, ele contêm inteligência e portanto torno o seu objeto em algo não-anémico.
Ok, vamos supor o seguinte: No banco de dados existe um campo raio. O que significa que o FP usará o atributo raio ou os respectivos getters e setters. Mas, em todo o sistema se trabalha com o diâmetro. Sim, isso é uma inconsistência, mas vamos supor que seja porque o banco de dados é legado ou pertence a terceiros, por exemplo. Pois bem, como o sistema trabalha com diâmetro mas persiste o raio, eu poderia colocar um par de getters e setters para o diâmetro, centralizando a regra de negócio (conversão de raio para diâmetro e vice-versa) no objeto. Mas, se eu não criasse esses getters e setters, o que aconteceria? Eu seria obrigado a pulverizar a regra de conversão em todo o sistema ou então criar algum outro objeto apenas para fazê-la, quando seria muito mais simples, mais fácil E MAIS OO se o próprio objeto soubesse fazê-la.
Existem mais opções que essas que considerou. Partindo agora do principio que se trata de um banco legado ( coisas que eu não parti no post anterior) vc não pode fugir do contrato que o sistema legado tem. Mas vc pode criar mediadores entre esse contrato e o contrato da sua nova aplicação.
Se o objeto original requer um raio porque é o que vai para o banco via introspecção direta do atributo ( eu tinha entendido que o FP lia o get e usada o set ) não vejo problema em criar o get/setDiametro. ( Eu tinha entendido que criar essa propriedade fazia o FP tentar ler e escrever essa propriedade no banco... se isso não acontece, não ha problema alguma)
Vc tem razão, não se pulveriza a regra, criam-se novos métodos.
sergiotaborda wrote:
Muitos frameworks leem direto dos campos, mas isso é falho. Experimente usá-los em ambiente seguro e terá uma grande surpresa. O erro é do cara que modelou o diâmetro como uma propriedade quando claramente não é. Até UML tem um símbolo especial para campos desse tipo ("calculados"  . Não é nada do outro mundo. E a convenção é muito simples. Não ha modificador e o acesso não começa com get.
Neste caso, você está afirmando que se toda a minha lógica de negócio for em cima do diâmetro, mas por algum motivo qualquer o BD trabalhe com o raio e isso não possa ser mudado, então eu vou ser obrigado a mudar toda a lógica do meu sistema apenas para satisfazer um desejo do BD porque o raio deveria ser uma propriedade e não o diâmetro?
Não. O que estou dizendo é que se o diametro é uma consequencia do raio, ele não é uma propriedade. No seu caso, ele é uma propriedade "paralela" ao raio. Mas sendo que o seu FP acessa os atributos diretamente os métodos que existem são irrlevantes.
Se o FP acessasse via get/set como falei ( que é o correto) a existencia de uma propriedade "virtual" como diametro causaria problemas. Mas está certo causar problemas já que ele não é uma propriedade real.
O que eu faria para resolve isso é criar um novo objeto herdado do primeiro mas com as novas propriedades ( padrão Decorator)
Para o FP eu mandava o objeto, como é da mesma classe, não teria problema, e o FP me dá o VO original o qual trasnformo no novo VO. Isso é simples se existir um camada de repositorios ou daos especificos de negocio, mas caso não exista pode, realmente, ser um problema. (é nestes casos que a gente ve se o sistema foi bem desenhado/encapsulado) Nesse caso, em que a unica alternativa é usado o VO original com novos métodos, simplesmente não usaria o get/set , faria algo como diamtero() e atribuiDiamtero() para fugir da nomenclatura. contudo isso interfere com a UI, provavelmente. Nesse caso seriam os objetos da UI a terem que ser mudados.
Fazer refactoring em sistemas legados não é fácil. as suas opções são limitadas e freqüentemente herança é a unica saida porque vc quer que o novo objeto se comporte como o velho, mas com mais alguma coisa. (padrão Decorator , Visitor e em alguns casos Proxy e Adapter)
sergiotaborda wrote:
Terceiro erro:
Ok, joguemos no lixo os frameworks que trabalham com atributos e troquemos por frameworks que usam getters e setters. Essa abordagem força você a colocar getters e setters em tudo, o que provavelmente torna a maior parte dos campos como se fossem públicos e se não fizer isso, o framework explodirá com um belo NoSuchMethodException.
O que é corretíssimo.
Não ha nenhum problema em colocar get/set porque isso lhe permite definir outras coisas que não são propriedades. Os frameworks de persistencia só se interessam pelas propriedades então se o objeto está bem modelado, não vai haver problema nenhum.
É correto eu compremeter o encapsulamento do meu objeto só porque o FP precisa disso?
Eu tinha entendido que o seu FP acessava via get/set e que isso lhe causava um problema quando adicionava o get/setDiametro.
Nesse caso o problema não era do FP e sim do contrato do seu objeto. Foi isso que tentei explicar.
Agora, se o FP acessa os atributos directamente via reflection e ignora os get/set então é o FP que está errado.
Veja, vc inclui campos privados e modificadores e acessores publicos para proteger os dados. Mas ai o seu FP viola as regras e vai direto no coração da coisa. Isso é uma violação clara do encapsulamento. Tlv vc não se importe com isso, mas mais valai os seus atributos serem simplesmente publicos e pronto: fim do problema de encapsulamento. Aceitar as duas coisas - objeto protegido e FP com reflection - não é coerente. Se vc sabe que o FP é introspectivo nos campos directamente nem precisa de set e get.
não é correto comprometer o encapsulamento em hipotese alguma. Mais vale tirá-lo do que viver em um mundo fingido de proteção. Se o FP precisa disso e não ha como alterar o FP , retire o encapsulamento em vez. O problema em legado é que a estrutrua já foi feita e se foi feita torta agora não tem como a endireitares ( não se muito esforço mental para arrajnar truques de OO que funcionam, mantem o sentido legado mas são OO)
Vamos supor que eu NÃO CONFIE no BD. Vamos supor que o número de parcelas deve sempre ser positivo, mas vamos dizer também que no passado havia algum bug que fazia com que em alguns registros surgisse um número de parcelas negativo. Se no meu setter eu colocar um if (numeroParcelas <= 0) throw new IllegalArgumentException(), qual é o resultado? O FP explode.
Isso é normal. Não é o FP que explode, é o setter. O FP simplesmente reage a isso.
Até que está correto em ele explodir, pois está trabalhando com algo inválido.
Exatamente!
O problema é que isso por sua vez pode comprometer a robustez e deixar o sistema extremamente intolerante a falhas (inclusive impedindo que o usuário possa retificar o valor sem acessar o BD diretamente).
O sistema não deve ser tolerante a esse tipo de falhas porque isso é uma violação de segurança. O sistema não deve ignorar isso.
Imagine que foi alguem que entrou no banco e correu um virus que simplesmente troca o sinal de todos os numeros. O sistema não pode tolerar isso. É um desastre. é melhor que ele pare de funcionar do que funcionar errado.
Isso força eu simplesmente deixar que o objeto fique inválido ou então fazer o setter falhar silenciosamente ou setar algum campo para avisar que o objeto é defeituoso ou qualquer outro tipo de gambiarra apenas para remendar o problema.
Mas ai é que está. Inválido é diferente de Incosistente. O seu objeto está consistente mesmo com valor negativo. Só que ele não está válido. O FP pode retornar objetos inválidos, o que ele não pode fazer é criar objetos inconsistentes. Se a regra no set é destinada a evitar problemas de leitura de registros com dados impossiveis, isso é consistencia e está certo que tudo dê pau e o sistema como um todo aborte. ( na real, um log é gerado e uma mensagem amigável é enviada ao usuário, o resto do sistema continua funcionando -isso é tolerancia a erro , tb)
sergiotaborda wrote:
Se assim não fosse eles estariam a) violando o encapsulamento ou b) inferindo regras de construção do dominio. FP não podem fazer nenhuma dessas duas coisas. Sim, é claro que poderiam usar um factory ou um método estático como mais um arsenal mas... na real, qual é o programador que quer fazer isso ? A maioria nem sabe declarar o construtor direito ...
Concordo com quase tudo disso, especialmente se o framework pudesse usar uma factory seria muito legal. Só discordo no ponto de "na real, qual é o programador que quer fazer isso ? A maioria nem sabe declarar o construtor direito ... " Se eu estiver criando um objeto Semáforo que já venha em um estado "Verde, amarelo ou vermelho", sou obrigado a criá-lo sem estado para depois dar um setCor nele?
Vc está limitando muito as suas opções pensando assim. Vc pode simplesmente ter um construtor privado e ter métodos que lhe retorna o semaforo no estado correto( factory method) Por exemplo:
não ha construtor , nem set. Só um get porque a cor é uma propriedade read-only.
É com este tipo de objeto corretamente estruturado que os FP da vida têm problemas.
E eles os têm porque não permitem que factory decida como criar o objeto a partir dos dados do registro.
Ou seja, na verdade eu seria obrigado a colocar a validação em um outro objeto, quando na verdade ela deveria estar no objeto em questão. Mas, isso mostra uma direção a seguir para resolver o problema. Se o VO fosse um objeto que simplesmente fosse usado para isolar o BD e mais nada, o objeto de verdade teria a validação dentro dele mesmo.
O objeto NUNCA tem a validação dentro dele mesmo. Ele têm a consistência. Por exemplo, um objeto BigDecimal pode ser positivo, zero ou negativo. Ele consiste o seu estado para que assim seja. Contudo ele não sabe nada sobre validação já que isso é uma regra externa. Um objeto com a responsabilidade de validar precisa ser criado à parte.
Então, sim, vc cria outro objeto, mas não, esse objeto não é de entidade.
Sim, o conceito de propriedades diz que elas devem ser independentes, mas nem sempre isso é possível porque as regras de negócio podem exigir coisas estranhas. E quando isso acontece, fazer o que? Sentar e chorar? Socar gambiarra?
Não. Usar OO. O objeto de dados pode ter os dados que ele quiser. O objeto de validação irá validar, se esses dados, estão em conformidade para uma situação especifica. Isso normalmente inclui regras de negocio que podem até ser muito complexas.
A criação de objetos de validação em separado do objeto do dados (SoC) permite que vc escolha o algoritmo de validação e o altere sem mexer no objeto de dados (IoC). E pode até compor os validadores( composite). Assim o validador de objeto cliente é a compsição de validadores singulares para cada campo. E esses são muito reproveitáveis ( isNull, isInRange, hasSize , etc. )
sergiotaborda wrote:
Pior ainda no caso de acesso direto aos atributos: não há forma de evitar que o objeto entre em estado inválido. Também não é possível lançar exceções de dentro do construtor e nem fazer qualquer tipo de delegação. O framework também nunca avisará que o objeto está pronto e que nenhum outro atributo ou setar será usado, o que impossibilita fazer a verificação de consistência após o framework fazer o trabalho.
(...)
O objeto pode até entrar em estado inconsistente ( pensemos que sim, para continuar o argumento) mas o FP nunca lhe retornará esse objeto. Ele vai dar exception antes.
Portanto, mesmo que ele entre em estado inconsistente, isso não será problema.
( Supondo que o FP está construido corretamente. Se não está é um problema do framework, não do padrão VO ou do padrão Bean)
Errado, o problema é exatamente que NÃO vai ocorrer nenhum problema e NÃO vai dar nenhuma exception. Para o FP é como se esses atributos fossem públicos e ele fará o que bem entender com eles e ninguém conseguirá impedi-lo. O máximo que você poderá fazer é verificar se o que o FP produziu faz sentido, mas não há nada que possa impedi-lo de criar e retornar tal objeto.
Tudo bem. Não é responsabildiade do FP validar o objeto. Se o sistema aceita que objeto seja modificado via reflection isso signfiica que qualquer coisas é um estado consistente (que é o mesmo que dizer que não ha consistencia alguma).
Vc pode resolver isso facilmente com um objeto validador para interomper as coisas se os dados não podem ser aceites.
Em um mundo perfeito (o modelador e o DBA são a mesma pessoa) sim, dá para dizer que esse problema acabaria sendo causa de erro da modelagem. Mas, deixa eu ver se entendi, você acha que separar o estado do objeto da sua lógica é algo bom?
Não. O que eu acho é que o pessoal escolhe errado na hora de definir o que é "a lógica do objeto".
Por exemplo, lançar métodos de crud no objeto é considerado por alguns a "lógica do objecto" quando isso não poderia ser mais contrário ao que seria a "lógica do objecto". O exemplo bom é o BigDecimal. Vc pode usá-lo como VO e ele não contem nenhuma lógica do negocio. Contudo ele contém muita lógica do seu domínio (operações aritméticas, conversões ,etc).
Poderíamos dizer que BigDecimal é um objeto cheio de "lógica de objeto" mas vazio de "lógica de sistema"
eu acho que vc deve criar objetos ricos no sentido que eles contenham "lógicas de objeto' e até lógica de domínio (logicas de sistema comuns a muitos sistemas) mas colocar a lógica do sistema nos objetos deve obedecer ao principio de separação de responsabilidade e isso significa que entulhar todas as logicas num objeto só tb não e´certo ( pro exemplo, colocar um metodo no usuário para validar o login e senha dele). É ai que entra a verdadeira OO , é ai que o desenvolvedor tem que mostrar porque tem esse papel e é ai que as coisas "acontecem".
dividir as coisas entre "objetos com lógica" e "objetos sem lógica" ainda é deficiente. Isso leva à prática ( que vemos no GUJ ser referida ad naseum) de saturar o objeto com um mote de métodos de negocio. ie, um monte de responsabilidades que não lhes competem. É aqui que entra a separação clara de Entidades, Agregados, Valores e Serviços e demais objetos de aplicação com responsabilidades bem definidas. Isso é o Principio de Separação de Responsabilidade em ação.
sergiotaborda wrote:
A situação fica ainda pior quando a camada de apresentação e de negócio estão em JVMs distintas, pois nesse caso o padrão TO é recomendado. O TOs normalmente é (e deve) ser modelado como um objeto com getters burros (o que implica atributos praticamente refletindo os getters). Mas por questão de reusabilidade, uma vez que o VO nada mais é do que o TO com os setters a mais, o VO acaba assumindo o lugar do TO.
Essa ai nem existe. Se o seu VO é Serializable ( o que não é dificil) ele pode ser usando inter-JVM sem problemas. O problema ocorre quando vc expoe uma interface publica com esses objetos como num webservice, por exemplo. Ai vc precisa da conversão para isolar a camada de apresentação ( webservice) do sistema em si. Mas fora isso , não ha problema nenhum em usar VO Serializáveis no lugar de TO ...
Por si só isso não é um problema. O problema é que isso acaba criando a falsa idéia de que o uso das VOs é legítimo e dificulta o trabalho de eliminá-las, simplesmente porque isso faz com que elas aparentemente simplesmente não possam ser eliminadas.
Ora ai é que está. O uso de VO é legitimo. Mas como toda a legimitidade advém do consenso.
Quando à tempos atrás isso era papagaiado pela sun e todo o mundo aceitável era legitimo. Hoje com essa conversa de que objetos precisam ter lógicas ele é ilegitimo para quem pensa dessa forma.
Para mim é uma questão de contexto. Pense num webservice : nada é mais do que um grafo de VO colocado em XML e recontiuido do outro lado. Neste caso é legitimo. Imagine uma API e interface que o seu programa deixa livre para uso por scripts. Ai tb é legitimo pq vc precisa de isolamento. Quando vc trabalha em difernetes JVM mas a comunicação é interna ao ppr sistema ( por exemplo, swing acessando ejb noutr máquina) tb é legitimo. Existem muitos lugares e ocasiões em que é legitimo usar VO (atualemnte chamados TO- Transfer Objet).
Forçar que todos os objetos do seu sistema sejam "não-VO" ou "não-anémicos" também é um erro. A virtude está no meio, sendo alguns VO e outros não dependendo das suas responsabilidades.
Veja, no seu sistema de FP os seus VO têm qual responsabilidade ? Transferir dados.
Transferir dados é tão honrado quando manipular dados ou comunicar protocolos. Não ha nada de errado em que objetos tenham apenas uma responsabilidade. O desafio é que todos os objetos de todo o sistema tenham apenas uma responsabilidade.
Ficar gritando que les deveria ter mais responsabilidade sem saber qual ela seria é vazio.
E você se esqueceu do principal: Ter bases de dados e/ou aplicações legadas ou de terceiros. E também frameworks que não entendem diametro() e exigem getDiametro() (que como você sabe, remete aos JavaBeans). Outra coisa é o setDiametro. Você usaria setRaio(valor * 2) em TODOS os lugares que precisassem setar o diâmetro?
Até poderia aceitar isso se todas as outras opções falhassem, mas a minha tendencia seria usar um Decorator para não ter que fazer isso. Mas apenas se fosse realmente muito importante. ( o exemplo do diametro é um bom exemplo, mas não parece muito importante... entende?)
sergiotaborda wrote:
Não são os frameworks que são burros. São os seus idealizadores e o seus utilizadores que são ineptos.
É claro que inépcia é uma coisa passageira, basta que as pessoas estudem mais. Algumas outras doenças mais graves e não são tão passageiras... ( como a preguiça , por exemplo...)
Eu não disse que todos os frameworks são burros. Eu disse que há muitos frameworks burros (e isso você tem que concordar).
sim, concordo. Principalmente porque a maioria vai directo nos atributos e ignora os acessores/modificadores. (existe uma razão para se chamarem "acessores" ... todo o acesso é feito por eles!)
This message was edited 4 times. Last update was at 01/09/2008 12:20:33
|
Criando sua própria API de Validação
Blog do MiddleHeaven |
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 01/09/2008 12:36:33
|
peczenyj
Moderador
![[Avatar]](/images/avatar/299dc35e747eb77177d9cea10a802da2.jpg)
Membro desde: 26/03/2006 23:25:37
Mensagens: 3191
Localização: Rio de Janeiro
Offline
|
- O que leva as pessoas a criar VOs anêmicas?
A pergunta deveria ser
Quem lucra (e como) com pessoas que criam VOs anêmicas?
|
http://pacman.blog.br
'Não importa quanto alguém se dedique à tarefa. Ninguém consegue fazer a água da cascata cair para cima.' |
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 01/09/2008 13:23:33
|
sergiotaborda
GUJ Expert
![[Avatar]](/images/avatar/b4a0e0fbaa9f16d8947c49f4e610b549.png)
Membro desde: 22/03/2005 20:57:48
Mensagens: 3433
Offline
|
peczenyj wrote:- O que leva as pessoas a criar VOs anêmicas?
A pergunta deveria ser
Quem lucra (e como) com pessoas que criam VOs anêmicas?
Quem lucra são pessoas que dizem assim: "Classe anêmicas é sempre ruim".
Depois criam consultorias para modificar o seu sistema para não usar classes anêmicas.
ai vc, crente de que é ruim ( ou com medo que seja ruim) contrata essas consultorias para fazer as alterações.
Depois - ou em paralelo - contrata cursos com elas para ensinarem seu staf a não usar classes anêmicas.
Como é mentira que classes anêmicas são ruins sempre o staf não compreende como o novo paradigma pode ser usado
em alguma ocasião e a consultoria é chamada de novo para resolver o problema. Afinal, o staf é uma cambada de idiotas
e as consultorias que advogam a expurgação do demônio das classe anêmica são as únicas com a verdade.
Detalhe... foram essas mesmas consultorias que criaram as classes anêmicas do sistema quando o paradigma
aceitava e até favorecia isso. É assim que se ganha dinheiro com isso. Muda-se o paradigma e refaz-se tudo de novo.
E cobra-se por isso, caro... perdão... claro.
|
Criando sua própria API de Validação
Blog do MiddleHeaven |
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 01/09/2008 14:08:18
|
victorwss
JWizard
![[Avatar]](/images/avatar/4ab232445f9b21b65dfdf6ea5f27f704.png)
Membro desde: 18/12/2007 14:46:00
Mensagens: 2409
Localização: São Paulo - SP
Offline
|
sergiotaborda wrote:Existem mais opções que essas que considerou. Partindo agora do principio que se trata de um banco legado ( coisas que eu não parti no post anterior) vc não pode fugir do contrato que o sistema legado tem. Mas vc pode criar mediadores entre esse contrato e o contrato da sua nova aplicação.
Se o objeto original requer um raio porque é o que vai para o banco via introspecção direta do atributo ( eu tinha entendido que o FP lia o get e usada o set ) não vejo problema em criar o get/setDiametro. ( Eu tinha entendido que criar essa propriedade fazia o FP tentar ler e escrever essa propriedade no banco... se isso não acontece, não ha problema alguma)
Vc tem razão, não se pulveriza a regra, criam-se novos métodos.
Na verdade, eu estava falando dos dois.
Há frameworks que vão direto nos atributos.
Há frameworks que sempre usam getters e setters.
Há frameworks que não são nenhum dos dois anteriores (usam annotations, Factory Methods, ou alguma outra coisa).
sergiotaborda wrote:
Não. O que estou dizendo é que se o diametro é uma consequencia do raio, ele não é uma propriedade. No seu caso, ele é uma propriedade "paralela" ao raio. Mas sendo que o seu FP acessa os atributos diretamente os métodos que existem são irrlevantes.
Se o FP acessasse via get/set como falei ( que é o correto) a existencia de uma propriedade "virtual" como diametro causaria problemas. Mas está certo causar problemas já que ele não é uma propriedade real.
Na verdade, não vejo como a propriedade virtual possa causar problemas em um framework que trabalhe com getters e setters. A menos que ele faça algumm tipo de idiotice como ligar o nome dos getters e setters aos nomes dos campos, mas supondo que não faça isso, não vejo como poderia dar problema.
sergiotaborda wrote:
O que eu faria para resolve isso é criar um novo objeto herdado do primeiro mas com as novas propriedades ( padrão Decorator)
Para o FP eu mandava o objeto, como é da mesma classe, não teria problema, e o FP me dá o VO original o qual trasnformo no novo VO. Isso é simples se existir um camada de repositorios ou daos especificos de negocio, mas caso não exista pode, realmente, ser um problema. (é nestes casos que a gente ve se o sistema foi bem desenhado/encapsulado) Nesse caso, em que a unica alternativa é usado o VO original com novos métodos, simplesmente não usaria o get/set , faria algo como diamtero() e atribuiDiamtero() para fugir da nomenclatura. contudo isso interfere com a UI, provavelmente. Nesse caso seriam os objetos da UI a terem que ser mudados.
Fazer refactoring em sistemas legados não é fácil. as suas opções são limitadas e freqüentemente herança é a unica saida porque vc quer que o novo objeto se comporte como o velho, mas com mais alguma coisa. (padrão Decorator , Visitor e em alguns casos Proxy e Adapter)
Concordo.
sergiotaborda wrote:
Eu tinha entendido que o seu FP acessava via get/set e que isso lhe causava um problema quando adicionava o get/setDiametro.
Nesse caso o problema não era do FP e sim do contrato do seu objeto. Foi isso que tentei explicar.
Agora, se o FP acessa os atributos directamente via reflection e ignora os get/set então é o FP que está errado.
Veja, vc inclui campos privados e modificadores e acessores publicos para proteger os dados. Mas ai o seu FP viola as regras e vai direto no coração da coisa. Isso é uma violação clara do encapsulamento. Tlv vc não se importe com isso, mas mais valai os seus atributos serem simplesmente publicos e pronto: fim do problema de encapsulamento. Aceitar as duas coisas - objeto protegido e FP com reflection - não é coerente. Se vc sabe que o FP é introspectivo nos campos directamente nem precisa de set e get.
não é correto comprometer o encapsulamento em hipotese alguma. Mais vale tirá-lo do que viver em um mundo fingido de proteção. Se o FP precisa disso e não ha como alterar o FP , retire o encapsulamento em vez. O problema em legado é que a estrutrua já foi feita e se foi feita torta agora não tem como a endireitares ( não se muito esforço mental para arrajnar truques de OO que funcionam, mantem o sentido legado mas são OO)
Não. Vamos supor que o FP vá direto aos atributos. Para o FP, beleza, é como se fosse público. Mas isso não significa que eles devem ser públicos para o resto da aplicação, caso os VOs sejam reutilizados em outro ponto.
sergiotaborda wrote:
O sistema não deve ser tolerante a esse tipo de falhas porque isso é uma violação de segurança. O sistema não deve ignorar isso.
Imagine que foi alguem que entrou no banco e correu um virus que simplesmente troca o sinal de todos os numeros. O sistema não pode tolerar isso. É um desastre. é melhor que ele pare de funcionar do que funcionar errado.
Eu não disse que ele deve ignorar, mas ele não também não deve simplesmente explodir e parar de funcionar. Deve ao menos solicitar para que o usuário corrija o valor errado.
sergiotaborda wrote:
Mas ai é que está. Inválido é diferente de Incosistente. O seu objeto está consistente mesmo com valor negativo. Só que ele não está válido. O FP pode retornar objetos inválidos, o que ele não pode fazer é criar objetos inconsistentes. Se a regra no set é destinada a evitar problemas de leitura de registros com dados impossiveis, isso é consistencia e está certo que tudo dê pau e o sistema como um todo aborte. ( na real, um log é gerado e uma mensagem amigável é enviada ao usuário, o resto do sistema continua funcionando -isso é tolerancia a erro , tb)
Certo.
sergiotaborda wrote:
Vc está limitando muito as suas opções pensando assim. Vc pode simplesmente ter um construtor privado e ter métodos que lhe retorna o semaforo no estado correto( factory method) Por exemplo:
não ha construtor , nem set. Só um get porque a cor é uma propriedade read-only.
É com este tipo de objeto corretamente estruturado que os FP da vida têm problemas.
E eles os têm porque não permitem que factory decida como criar o objeto a partir dos dados do registro.
Excelente idéia. Porém nem o framework que trabalha com atributos e nem o que trabalha com getters e setters vão conseguir digerir isso porque o construtor sem argumentos não é público. Apenas os frameworks mais avançados vão saber lidar com isso.
sergiotaborda wrote:
O objeto NUNCA tem a validação dentro dele mesmo. Ele têm a consistência. Por exemplo, um objeto BigDecimal pode ser positivo, zero ou negativo. Ele consiste o seu estado para que assim seja. Contudo ele não sabe nada sobre validação já que isso é uma regra externa. Um objeto com a responsabilidade de validar precisa ser criado à parte.
Então, sim, vc cria outro objeto, mas não, esse objeto não é de entidade.
...
Não. Usar OO. O objeto de dados pode ter os dados que ele quiser. O objeto de validação irá validar, se esses dados, estão em conformidade para uma situação especifica. Isso normalmente inclui regras de negocio que podem até ser muito complexas.
A criação de objetos de validação em separado do objeto do dados (SoC) permite que vc escolha o algoritmo de validação e o altere sem mexer no objeto de dados (IoC). E pode até compor os validadores( composite). Assim o validador de objeto cliente é a compsição de validadores singulares para cada campo. E esses são muito reproveitáveis ( isNull, isInRange, hasSize , etc. )
É, você tem razão nestes dois pontos.
sergiotaborda wrote:
Tudo bem. Não é responsabildiade do FP validar o objeto. Se o sistema aceita que objeto seja modificado via reflection isso signfiica que qualquer coisas é um estado consistente (que é o mesmo que dizer que não ha consistencia alguma).
Vc pode resolver isso facilmente com um objeto validador para interomper as coisas se os dados não podem ser aceites.
Sim.
sergiotaborda wrote:
Não. O que eu acho é que o pessoal escolhe errado na hora de definir o que é "a lógica do objeto".
Por exemplo, lançar métodos de crud no objeto é considerado por alguns a "lógica do objecto" quando isso não poderia ser mais contrário ao que seria a "lógica do objecto". O exemplo bom é o BigDecimal. Vc pode usá-lo como VO e ele não contem nenhuma lógica do negocio. Contudo ele contém muita lógica do seu domínio (operações aritméticas, conversões ,etc).
Poderíamos dizer que BigDecimal é um objeto cheio de "lógica de objeto" mas vazio de "lógica de sistema"
eu acho que vc deve criar objetos ricos no sentido que eles contenham "lógicas de objeto' e até lógica de domínio (logicas de sistema comuns a muitos sistemas) mas colocar a lógica do sistema nos objetos deve obedecer ao principio de separação de responsabilidade e isso significa que entulhar todas as logicas num objeto só tb não e´certo ( pro exemplo, colocar um metodo no usuário para validar o login e senha dele). É ai que entra a verdadeira OO , é ai que o desenvolvedor tem que mostrar porque tem esse papel e é ai que as coisas "acontecem".
dividir as coisas entre "objetos com lógica" e "objetos sem lógica" ainda é deficiente. Isso leva à prática ( que vemos no GUJ ser referida ad naseum) de saturar o objeto com um mote de métodos de negocio. ie, um monte de responsabilidades que não lhes competem. É aqui que entra a separação clara de Entidades, Agregados, Valores e Serviços e demais objetos de aplicação com responsabilidades bem definidas. Isso é o Principio de Separação de Responsabilidade em ação.
Sim, com certeza, colocar toda a lógica é pior do que não colocar nenhuma.
sergiotaborda wrote:
Ora ai é que está. O uso de VO é legitimo. Mas como toda a legimitidade advém do consenso.
Quando à tempos atrás isso era papagaiado pela sun e todo o mundo aceitável era legitimo. Hoje com essa conversa de que objetos precisam ter lógicas ele é ilegitimo para quem pensa dessa forma.
Para mim é uma questão de contexto. Pense num webservice : nada é mais do que um grafo de VO colocado em XML e recontiuido do outro lado. Neste caso é legitimo. Imagine uma API e interface que o seu programa deixa livre para uso por scripts. Ai tb é legitimo pq vc precisa de isolamento. Quando vc trabalha em difernetes JVM mas a comunicação é interna ao ppr sistema ( por exemplo, swing acessando ejb noutr máquina) tb é legitimo. Existem muitos lugares e ocasiões em que é legitimo usar VO (atualemnte chamados TO- Transfer Objet).
Forçar que todos os objetos do seu sistema sejam "não-VO" ou "não-anémicos" também é um erro. A virtude está no meio, sendo alguns VO e outros não dependendo das suas responsabilidades.
Veja, no seu sistema de FP os seus VO têm qual responsabilidade ? Transferir dados.
Transferir dados é tão honrado quando manipular dados ou comunicar protocolos. Não ha nada de errado em que objetos tenham apenas uma responsabilidade. O desafio é que todos os objetos de todo o sistema tenham apenas uma responsabilidade.
Sim, ele é legítimo para transferir dados. O problema das arquiteturas anêmicas é usá-los também como entidade.
sergiotaborda wrote:
Ficar gritando que les deveria ter mais responsabilidade sem saber qual ela seria é vazio.
Inventar necessidades sem ter porque é uma péssima idéia em todos os casos. Não entendi o que você quis dizer com isso.
sergiotaborda wrote:
Até poderia aceitar isso se todas as outras opções falhassem, mas a minha tendencia seria usar um Decorator para não ter que fazer isso. Mas apenas se fosse realmente muito importante. ( o exemplo do diametro é um bom exemplo, mas não parece muito importante... entende?)
Sim, problemas simples devem ter soluções simples, problemas complexos talvez precisem de soluções complexas.
sergiotaborda wrote:sim, concordo. Principalmente porque a maioria vai directo nos atributos e ignora os acessores/modificadores. (existe uma razão para se chamarem "acessores" ... todo o acesso é feito por eles!)
Sim, e acrescento. Os frameworks que exigem getters e setters para tudo e morrem de forma sangrenta e dolorosa se estes não forem burros como eles esperam que são também são ruins, eles forçam você a criar o falso encapsulamento de get e set para tudo.
Ok, até aqui acho que concordamos no mais importante: O modelo anêmico tem origem na persistência e fugir disso é algo trabalhoso. O que vejo que você não concorda muito é se deve-se ou não fugir dele.
This message was edited 1 time. Last update was at 01/09/2008 14:10:48
|
Victor Williams Stafusa da Silva
Bacharel em Ciência da Computação - UFMT // Especialista em Desenvolvimento Java - CEFET/MT // Doutorando em Ciência da Computação - IME-USP
SCJP 6.0 - 19/12/2007 - PASS - 88% // SCWCD 5 - 17/05/2008 - PASS - 79% // SCJA - 09/09/2008 - PASS - 96% // SCSNI - 30/06/2009 - PASS - 68% // SCBCD 5 - 31/05/2010 - PASS - 95%
Próximos: SCJD (encalhado com o projeto), SCEA parte I (estudando). Algum dia desses: SCMAD, OCA, SCEA e SCDJWS.
Computação: uma ciência holística e esotérica!
E então veio Deus a terra e disse aos homens: Não dividireis por zero.
XML is a giant step in no direction at all. (Erik Naggum)
Arquitetura de sistemas: Eu prefiro ser essa metamorfose ambulante do que ter aquela velha opinião formada sobre tudo.
Diga não as drogas: Não use java.util.Vector.
Cuidado: Este usuário pode ter temperamento agressivo.
Always code as if the person who will maintain your code is a maniac serial killer that knows where you live.
I am the maniac serial killer that knows where you live who will maintain your code.
É impossível falar de CMMI (Capability Maturity Model Integration) sem saber o que é CIMM (Capability Im-Maturity Model).
Se você escreve "concerteza", "concerteza" você andou matando aulas de português. |
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 01/09/2008 14:47:54
|
sergiotaborda
GUJ Expert
![[Avatar]](/images/avatar/b4a0e0fbaa9f16d8947c49f4e610b549.png)
Membro desde: 22/03/2005 20:57:48
Mensagens: 3433
Offline
|
victorwss wrote:
Na verdade, eu estava falando dos dois.
Há frameworks que vão direto nos atributos.
Há frameworks que sempre usam getters e setters.
Há frameworks que não são nenhum dos dois anteriores (usam annotations, Factory Methods, ou alguma outra coisa).
ah! bom ... afinal não estou louco. ...
sergiotaborda wrote:
Não. O que estou dizendo é que se o diametro é uma consequencia do raio, ele não é uma propriedade. No seu caso, ele é uma propriedade "paralela" ao raio. Mas sendo que o seu FP acessa os atributos diretamente os métodos que existem são irrlevantes.
Se o FP acessasse via get/set como falei ( que é o correto) a existencia de uma propriedade "virtual" como diametro causaria problemas. Mas está certo causar problemas já que ele não é uma propriedade real.
Na verdade, não vejo como a propriedade virtual possa causar problemas em um framework que trabalhe com getters e setters. A menos que ele faça algumm tipo de idiotice como ligar o nome dos getters e setters aos nomes dos campos, mas supondo que não faça isso, não vejo como poderia dar problema.
Sim. Supondo que o FP é dotado de mapeamento, isso não seria um problema.
sergiotaborda wrote:
não é correto comprometer o encapsulamento em hipotese alguma. Mais vale tirá-lo do que viver em um mundo fingido de proteção. Se o FP precisa disso e não ha como alterar o FP , retire o encapsulamento em vez. O problema em legado é que a estrutrua já foi feita e se foi feita torta agora não tem como a endireitares ( não se muito esforço mental para arrajnar truques de OO que funcionam, mantem o sentido legado mas são OO)
Não. Vamos supor que o FP vá direto aos atributos. Para o FP, beleza, é como se fosse público. Mas isso não significa que eles devem ser públicos para o resto da aplicação, caso os VOs sejam reutilizados em outro ponto.
Sim quer.
O objetivo se encapsular é encapsular sempre. Se existe um ponto onde não é encapsulado isso anula o encapsulamento como um todo. Logo, se a violação acontece no FP , ele pode acontecer em qualquer outro lugar ( uma violação a mais não é problema, o problema é a primeira violação).
Portanto, se vc está preocupado em encapsular no resto do sistema tb deveria estar no FP. Dois pesos e duas medidas não é coerente.
sergiotaborda wrote:
O sistema não deve ser tolerante a esse tipo de falhas porque isso é uma violação de segurança. O sistema não deve ignorar isso.
Imagine que foi alguem que entrou no banco e correu um virus que simplesmente troca o sinal de todos os numeros. O sistema não pode tolerar isso. É um desastre. é melhor que ele pare de funcionar do que funcionar errado.
Eu não disse que ele deve ignorar, mas ele não também não deve simplesmente explodir e parar de funcionar. Deve ao menos solicitar para que o usuário corrija o valor errado.
Mas ai que tá. O FP é um cara lá na ultima camada. ele não pode fazer isso que está pedindo. O máximo que ele pode fazer é lançar exceções que as camadas acima possam interpretar e levar à correção.
sergiotaborda wrote:
não ha construtor , nem set. Só um get porque a cor é uma propriedade read-only.
É com este tipo de objeto corretamente estruturado que os FP da vida têm problemas.
E eles os têm porque não permitem que factory decida como criar o objeto a partir dos dados do registro.
Excelente idéia. Porém nem o framework que trabalha com atributos e nem o que trabalha com getters e setters vão conseguir digerir isso porque o construtor sem argumentos não é público. Apenas os frameworks mais avançados vão saber lidar com isso.
Ora ai está. Esse são ainda são escassos.
sergiotaborda wrote:
Ora ai é que está. O uso de VO é legitimo. Mas como toda a legimitidade advém do consenso.
Sim, ele é legítimo para transferir dados. O problema das arquiteturas anêmicas é usá-los também como entidade.
Será mesmo isso ?
Quanto a mim não. Qaundo o Fowler se queixou do anemismo era porque os objetos não tinham lógicas relativas a eles mesmos - não ao sistema. O caso clássico é o calculo da idade em que o objeto mantêm a data de nascimento mas não sabe calcular idades. Então o calculo é espalhado pelo resto do código.
Até ha quem coloque isso num objeto Utils da vida, mas isso não resolve. O ponto é deixar o calculo da idade junto com o dado que define a idade. Isso torno o seu objeto de dados menos anêmico na perspectiva do Fowler, mas continua não o tornando uma entidade.
Quando vc pensa dessa forma vc descobre rapidamente que objetos especiais tem metodos especiais e recorrentes. Conta sempre tem saldo, mas o objeto Conta não tem um método saldo(). É isso que é o problema. Logicas que dizem respeito ao proprio objeto e que levam a codigos assim:
em vez de :
Isto é muiiiito difernete de codigos que extrapolam as responsabilidades como por exemplo
Que mata o sistema por muito fluente que pareça.
Então, é bom sublinhar o que significa ser anêmico: significa não ter consigo os métodos que dizem respeito (*) a si mesmo.
Isso é muito diferente de ter qualquer método anexado a si.
(*) Que são da responsabilidade de.
sergiotaborda wrote:
Ficar gritando que les deveria ter mais responsabilidade sem saber qual ela seria é vazio.
Inventar necessidades sem ter porque é uma péssima idéia em todos os casos. Não entendi o que você quis dizer com isso.
O quiz dizer é que não vale a pena fazer alarde que anêmico é ruim quando "anêmico" não está bem definido. Isso so atrapalha.
dizer "Esse seu objeto não faz nada. jogue um métodos ai" é inútil Nem todos os objetos aceitam métodos especiais.
Ok, até aqui acho que concordamos no mais importante: O modelo anêmico tem origem na persistência e fugir disso é algo trabalhoso. O que vejo que você não concorda muito é se deve-se ou não fugir dele.
Não acho que o modelo anêmico tenha origem na persistência embora, possa , até, concordar que o framework que usa na persistencia influencia bastante o modelo de dados. Eu acho que ele tem origem em falhas de modelagem por decisões erradas
dos desenvolvedores. O banco é um espectador ( exceto quando é legado) logo a responsabilidade recai no desenvolvedor que aceitou um codigo espalhado ( codigo esparguete). O problema real é se ele teve escolha. Se ele vez um modelo anemico mas não tinah escolha não ha realmente culpa, mas se ele pode escolher e escolher errado, ai sim ele tem que ser responsabilizado por um codigo ruim.
Não vejo o TO com maus olhos já que todos os objetos de entidade , são, por construção, TO.
Eles podem até ter uma logica a mais, mas essas logicas aparecem normalmente em objetos de valor como implementações de Money. Para entidades puras ( Pedido, Cliente, etc...) os métodos especiais são mais relativos a relacionamentos, mas podem ser realmente mais espertos como Conta.getSaldo(Date.today())). Só que essa esperteza tem um outro preço que escapa da conversa dos modelos anêmicos que passa pelo design da estrutura interna desses objetos e do ambiente onde eles vivem. Vc onsegue garantir que conta.getSaldo() funciona em qualquer ponto do sistema , mesmo quando o sistema é distribuído ? Garantir isso não é fácil. E é essa dificuldade que leva à criação dos Busines Objects, porque é mais facil.
Portanto, tudo se reduz à dificuldade em implementar um modelo denso e complexo de entidades de forma e performance ótima.
This message was edited 1 time. Last update was at 01/09/2008 14:51:09
|
Criando sua própria API de Validação
Blog do MiddleHeaven |
|
|
 |
|
|
|
|