rvf, software e mitos

Posts Tagged ‘orientação a objetos

Pare. Pense. Tente você mesmo responder esta pergunta antes de prosseguir com a leitura. O que vem a ser o padrão VO que vemos presentes prefixando nossas classes em nove de cada 10 sistemas que trabalhamos em Java ? Ahá, eu já imaginava que você soubesse… mas… sabe mesmo?

Bem, na verdade existem dois tipos de Value Objects, que são os criados pela Sun, para mascarar os problemas das primeiras versões das distribuições EJBs e o que faz sentido num paradigma O.O.. Em minha humilde opinião, o segundo é mais coerente, pois de fato favorece um padrão, do contrário do primeiro, que na verdade acabou virando um anti-pattern. Portanto, focaremos este simples post no segundo e ao final, fazendo uma ressalva ao primeiro.

De acordo com Martin Fowler, Eric Evans e outros evangelistas de um modelo de domínio rico, um Value Object é um simples objeto, usualmente com atributos que não referenciam outros objetos, imutáveis e sem identidade, pois são meramente representativos. Em outras palavras, um verdadeiro objeto de valor – O objeto vale mesmo alguma coisa.  Exemplos:

Numero: é um típico exemplo de um VO. O seu valor justifica sua existência. É imutável, pois você não consegue mudar os valores de um numero. Deve-se criar um novo para isso. Sua comparação não se resume em todos seus atributos, comparando apenas o valor o qual representa é suficiente.

Dinheiro: Philip Calçado, provavelmente baseado no exemplo de Fowler, dá um ótimo exemplo de VO falando do objeto Dinheiro. Um dinheiro, supomos aqui, Reais, possui um valor: dois, cinco, dez, vinte, cinqüenta, etc… na vida real, é um papel. Obviamente, alterar seu valor resultaria em rasura ou problemas com a policia, justificando assim sua imutabilidade, no entanto, se eu te empresto dez reais, não há necessidade de receber os mesmos dez reais de pagamento. Pode ser outra nota, tanto que tenha o mesmo valor.

Cor: Oras, uma cor vale, herm… uma cor! Por isso que para representar cores é muito melhor usar Enums, a propósito, tudo, ou a grande maioria do que é VO deveria ser possível de se representar utilizando Enumeradores.

Deu para entender a grande sacada do verdadeiro VO? Ele representa um valor, simples e intuitivo, certo? Muito próximo do que chamamos de primitivos, afinal, se os criadores de linguagens de programação conhecessem todos os VOs possíveis, não precisaríamos ter objetos explicitamente declarados para estes, pois nossos compiladores reconheceriam um simples R$ 10,30 no editor, sendo dez reais e trinta centavos.

Mas então, não era bem isso que você achava que era um VO? Bom, vejamos se eu adivinho: Pra você, um VO era uma estrutura, com getterns and setters, comumente utilizada para transportar valores entre camadas e camadas. Então, é bem esta mesmo a confusão. Este é o TO (Transfer Object) – uma adaptação do padrão Data-Transfer Object (também catalogado por Fowler) – O TO na verdade é um valor composto por vários atributos, serializado (é transportado entre camadas), servindo principalmente para minimizar o trafego de objetos numa rede.

Recapitulando o que você vê nos sistemas por aí, não são VOs e sim TOs! Mas e se eu te falar que a utilização deles normalmente ocasiona em redundância do seu modelo, você acreditaria? Então, é. A grande maioria dos sistemas que utilizam TOs, de duas uma: ou realmente não sabem o que estão fazendo ou não seguem o principio “você não vai precisar disso no futuro”. É balela. Como já disse, TOs são para trafegar objetos de JVMs distintas entre TIERS (dica: vide padrão memento para construir arquiteturas distribuidas com TOs, eficazes). Quantos destes sistemas aí realmente precisam fazer isso? A grande minoria, creio. Ter TOs em LAYERS é puro hype.

Layers vs Tiers.
Não precisa cortar seus pulsos se você concordava comigo desde o inicio, é que gosto de manter a interatividade nos meus post subestimando meu leitor. Afinal, este tipo de post só é util para quem está aprendendo e não para quem já sabe 😉

Siga-me, maldito.

Anúncios

Não, eu não sou nenhum guru em TDD, mas aos poucos estou notando a vantagem desta técnica que agrega muito mais do que simples junits ao seu projeto. TDD te fornece na verdade outra abordagem: o desenho do seu software de uma maneira mais desacoplada e coesa.

... um pouco de história: Era uma vez um jovem desenvolvedor empolgado em introduzir técnicas de testes ágeis em sua empresa, porem, foi barrado pelo seguinte motivo: Testes (senão, os feito por testadores) são uma segunda opção (precisa dizer que a primeira é o cronograma?).  Esta é a visão dos gerentes/gestores de projetos que não conhecem TDD. A única coisa que sabem a respeito é de que TDD se testa primeiro. Na verdade, alguém que pensa assim, não sei nem como chega a este raciocínio, pois duvido muito que eles entendam o que dizem -i.e. como assim testar primeiro algo que ainda nem existe?-

TDD, não deve ser considerado como uma prática de teste -apenas-, mas sim como uma técnica poderosa de desenho/construção de aplicações OO, E, DE QUEBRA, tu ganhas o teste de unidade. Simples assim. Para cada caso, um teste que falha, onde sua história só termina quando ele passa. Por completo! A principio a idéia parece um pouco radical, mas é isso mesmo: você deve construir um teste que falhe primeiro, (nem que seja para testar se o seu JUnit está presente no seu classpath!), para, então, você ir incrementando aos poucos, refatorando, e criando as classes, métodos, xpto…  que sejam necessários para o mesmo passar e testando. Vamos brincar com  isso em um exemplo simples, de Conta Corrente:

Criamos um novo projeto, e nele, um novo teste unitário (estou utilizando o JUnit 4):

package com.wordpress.robsonvf;

import org.junit.Assert;

import org.junit.Test;

public class ContaCorrenteTest {

@Test

public void umDepositoDeveAumentarOSaldoNoValorDoDeposito() {

Assert.assertTrue(false);

}

@Test

public void umSaqueDeveDiminuirOSaldoNoValorDoSaque() {

Assert.assertTrue(false);

}

@Test(expected=RuntimeException.class)

public void umSaqueSoEhPossivelSeTemSaldo() {

}

}

Esta é a abstração mais simples possível de conta corrente que consegui chegar, creio que para o exemplo seja suficiente. Rode este teste, e veja-o falhar descaradamente e sem dó: o primeiro passo rumo a TDD foi dado. O segundo é fazer isso tudo funcionar! Notaram que, quando eu disse “abstração mais simples possível de conta corrente” ficou claro que, eu comecei a desenhar meu software antes mesmo de criar uma única classe de negócio sequer? E ainda tem gente que duvida que simples testes unitários são tão claros quanto montanhas de documentos.

Vamos trabalhar então no método: umDepositoDeveAumentarOSaldoNoValorDoDeposito, o que este método precisa para viver numa conta corrente? Na minha abstração primária, ficou assim:

@Test

public void umDepositoDeveAumentarOSaldoNoValorDoDeposito() {

ContaCorrente cc = new ContaCorrente(200d);

double saldo = cc.getSaldo();

cc.deposita(100d);

Assert.assertTrue(saldo+100d == cc.getSaldo());

}

Precisa explicar? uma nova sessão de conta corrente, um registro histórico do saldo atual com o qual a conta foi criada, um deposito, e uma verificação: o saldo esperado é igual ao saldo da conta? Você já consegue rodar este teste? Não? Está esperando o que então para criar a classe ContaCorrente e os métodos que o sua IDE chora sem parar por eles não existirem? Se utiliza o eclipse/netbeans, basta ir clicando nos erros e gerando a classe, construtores, métodos…, faltaria só implementar. Este é o seu exercício antes de continuar com a leitura.

Implementou? Rode o teste agora. Passou? Ótimo! Seu primeiro teste usando TDD foi concluído com sucesso. Se quiser, faça isso com os outros métodos até todos passarem. (recomendo fazer isso antes de ver, logo abaixo, o restante do código).

O teste pronto:

package com.wordpress.robsonvf;

import junit.framework.Assert;

import org.junit.Test;

public class ContaCorrenteTest {

@Test

public void umDepositoDeveAumentarOSaldoNoValorDoDeposito() {

ContaCorrente cc = new ContaCorrente(200d);

double saldo = cc.getSaldo();

cc.deposita(100d);

Assert.assertTrue(saldo+100d == cc.getSaldo());

}

@Test

public void umSaqueDeveDiminuirOSaldoNoValorDoSaque() {

ContaCorrente cc = new ContaCorrente();

cc.deposita(100);

cc.saque(90d);

Assert.assertTrue(10 == cc.getSaldo());

}

@Test(expected=RuntimeException.class)

public void umSaqueSoEhPossivelSeTemSaldo() {

ContaCorrente cc = new ContaCorrente();

cc.deposita(200);

cc.saque(201);

}

}

A minha classe ContaCorrente (aposto que a sua ficou igualzinha!):

package com.wordpress.robsonvf;

public class ContaCorrente {

private double saldo;

public ContaCorrente() { }

public ContaCorrente(double d) {

this.saldo +=d;

}

public double getSaldo() {

return this.saldo;

}

public void deposita(double d) {

saldo +=d;

}

public void saque(double d) {

if ((saldo-d) < 0)

throw new RuntimeException(“Saldo insuficiente!”);

saldo -= d;

}

}

Aprimore mais a lógica de negócio desta classe, envolva outras classes, e não se esqueça: primeiro o teste, depois a especificação do negócio, e por fim, a implementação… E FUNCIONANDO…

Este post foi apenas um simples estimulo para quem está querendo aprender TDD, assim como eu. Espero que você adote esta pratica para seus novos algoritmos, e, que convença seu chefe de que TDD não é apenas teste unitário avulso, mas, sim, uma nova maneira de se pensar na hora de sair construindo software.

Até a próxima.. e que venha a Agile Weekend aqui em poa 🙂

Conversando com um conhecido estes dias, o mesmo disse que está passando por poucas e boas por causa de um projeto que, quando aparecem manutenções, são de cair os cabelos. Claro, o software deve ter sido criado por um projetista não muito interessante, no meio disso, algo mais sério me chamou a atenção: O sistema utiliza o Framework Struts 1 customizado por algum desenvolvedor que passou por ali. Ainda não entendeu? Isso significa que o sistema está preso a uma versão especifica de um framework (que diga-se de passagem, ninguém mais merece Struts, ainda mais, customizado!), onde qualquer atualização do mesmo acarretaria em verificar os pontos alterados e tentar atualizar, ou seja, fora de cogitação! E então, a ferramenta entra em processo de end of life.

Você não deve customizar um framework (ha não ser que sua intenção seja enviar patches ao projeto), eu sei que as vezes parece ser tentador, mas evite. Considerando que esteja trabalhando com uma linguagem OO, a melhor opção seria estender funcionalidades e especializar aquelas classes que você esta pensando seriamente em estrupar customizar hardcoded like.

É incrível pensar que herança significa reutilizar código e ainda querer dizer que sabe Orientação a Objetos. Quem diz isso garanto que faz coisas do tipo:

PessoaFisica extends Pessoa

PessoaJuridica extends Pessoa

Muito bonito, reutilizou dados em comum entre PF e PJ, mas e o principio de OO, para onde foi? Para o lixo! Você simplesmente disse que, uma pessoa física é filho do mesmo pai de uma pessoa jurídica, um ser humano pode ser cosiderado irmão de uma empresa? Epa! Tem algo errado aí. Sim, isso está absolutamente errado se tratando de OO. “Ahh, mas meu professor disse que reutilizar código é bom” – Ok! Mas ele esqueceu de dizer que, deve haver uma coerência no meio disso, onde a reutilização de código se torna conseqüência de um bom modelo orientado a objetos e não uma obrigatoriedade, onde você tem a classe ChuckNorris com todos os comportamentos e atributos possíveis no universo, e faz todas suas subclasses herdarem ela.

Prefira a composição!

Como eu resolveria o problema da PessoaFisica e PessoaJuridica? Simples, usando composição. Vamos supor que você estivesse usando a classe Pessoa apenas para reutilizar dados cadastrais, ótimo, crie a classe DadosCadastrais e faça ela compor todas as classes que utilizem estes dados.

Quando usar herança?

Quando você definitivamente está certo que sua classe é uma especialização de alguma outra classe, que BEM PROVAVELMENTE será provida via polimorfismo. Por exemplo: ControleRemotoUniversal extends ControleRemoto.

A reutilização de código acontece uma vez que você ganha a componentização, onde você transformará sua família de objetos em um componente que se comunica muito bem com os objetos internos e externos. Isso é reutilizar código.

Cuidado com o que você aprende na faculdade…


Anúncios

  • Nenhum
  • Adolfo: Muito bom este post. Acho que tudo isso pode ser resumido em uma única palavra: humildade (isso não significa não defender seu ponto de vista).
  • Adolfo: Olá Robson, Alguns modelos até consegui identificar em alguns projetos que já trabalhei... Com algumas coisas eu concordo e outras não... Q
  • milah: Eu tenho um Amazon L71. Até 2 meses atrás não tive problemas com ele. Já troquei a placa de lan dele, por uma que capta melhor wi-fi. Só que ago