Fique por dentro Este artigo til porque fornece algumas propostas para um melhor uso da orientao a objetos, a fim de que seja possvel deixar o cdigo menos acoplado, mais inteligente e limpo. Tambm buscaremos apresentar aspectos dos modelos anmico e de domnio, mostrando as principais diferenas na hora de optar por um deles. A mxima popular afirma que para bom entendedor, meia palavra basta. Seguramente tambm poderamos utiliz-la na nossa profisso e afirmar: para um bom programador, meio cdigo basta. Quando o cdigo bem escrito, qualquer programador pode compreend-lo sem grandes dificuldades e entende rapidamente a real funo da implementao em questo. Como diria Martin Fowler, qualquer um pode escrever um cdigo que o computador entenda. Bons programadores escrevem cdigos que os humanos entendem. Este o verdadeiro desafio do desenvolvedor. Neste contexto, o objetivo deste artigo, como o prprio ttulo sugere, propor aspectos que tornem o cdigo mais claro, de modo que ele possa ser compreendido com facilidade por outros profissionais. Para a criao de um cdigo limpo nos termos de Robert C. Martin, torna-se necessrio seguir algumas regras bsicas. Segundo este autor, a possibilidade de um fcil entendimento e manuteno decorre do cumprimento de determinadas regras. Existe uma significativa literatura referente ao tema cdigo limpo. Neste artigo faremos comentrios sobre o que o cdigo limpo e como alcan-lo, atravs de um breve resumo baseado no livro Clean Code, de Robert C. Martin. Neste livro, so descritas regras que definem, por exemplo, a nomenclatura adequada para classes, mtodos e atributos, o correto uso dos comentrios, a melhor formatao do cdigo, o tratamento de erros e at mesmo testes unitrios. Martin, mais conhecido como Uncle Bob, um grande nome na comunidade de desenvolvimento de software, trabalhando na rea desde 1970. Fundador e presidente da Object Mentor Inc, um dos 17 membros do Manifesto gil e publicou diversos artigos e livros sobre o assunto. Contudo, no adianta apenas ter um cdigo limpo e bem escrito, se o mesmo for aplicado a um modelo de domnio mal estruturado. Para resolver este problema sero abordadas as diferenas entre o modelo anmico e o modelo de domnio. Estes modelos so tipos de padres que o programador poder utilizar para o desenvolvimento das funcionalidades de seu respectivo projeto. Apesar disso, atualmente o modelo anmico considerado um anti-pattern, ou seja, ele um anti-padro. Como curiosidade, a maioria dos profissionais ainda insiste em utiliz-lo. Com base nisso, neste artigo procuraremos expor algumas razes que confirmem as limitaes deste modelo, e como alternativa, apresentaremos o modelo de domnio (domain model), que consiste numa viso e numa tcnica para lidar tanto com os casos de domnios mais simples at os altamente complexos. Para a definio correta do domnio, teremos como base o uso da prtica do DDD, tcnica criada por Eric Evans, autor do livro Domain-Driven Design e um lder nos estudos sobre design de software e modelagem 2
de domnio. Assim, sero propostas algumas sugestes para que o desenvolvedor possa criar cdigos claros, eficazes e de fcil compreenso para outros desenvolvedores. Como anda o seu cdigo? Em primeiro lugar, devemos ter, de forma clara, a ideia do que se entende por cdigo limpo (ver BOX 1). O cdigo limpo no deve ser apenas desejvel devido sua organizao, mas tambm pela facilidade com que outros profissionais podero futuramente manuse-lo. Quando um cdigo no est bom, ele s tende a piorar com o decorrer do tempo, pois o desenvolvedor que tiver de fazer alguma manuteno, possivelmente no o far com tanta organizao porque encontrou o trabalho inicial j desestruturado. como o exemplo do carro com um dos vidros quebrados. BOX 1. Cdigo Limpo Segundo Kent Beck, um dos integrantes do Manifesto gil, Cdigo Limpo (Clean Code), de forma resumida, o cdigo fcil de entender, fcil de modificar e fcil de testar. Foi realizada uma experincia nos Estados Unidos que consistia em deixar um carro trancado e com apenas uma das janelas quebradas numa esquina. Aps alguns dias, o mesmo j possua mais janelas quebradas. Ao longo de mais algumas semanas, o carro j estava todo amassado e depenado. Para comparao, outro carro foi deixado no mesmo local, s que dessa vez sem problema algum. Este automvel permaneceu assim por semanas e ao final da experincia, nenhum dano lhe foi causado. Com este exemplo queremos dizer que as pessoas tm zelo por aquilo que est arrumado, que est sob bons cuidados. O mesmo acontece com o cdigo. Quando o desenvolvedor escreve-o de qualquer forma, ou seja, sem nenhum padro, os outros profissionais que posteriormente manusearem seu cdigo iro danific-lo ainda mais. Em outras palavras, o profissional tende a perpetuar uma desestruturao j existente. Um cdigo limpo aquele que segue determinados padres e regras de implementao, desde a escolha do nome da classe, atributos e mtodos, at o uso correto da orientao a objetos. Para exemplificar esses padres e regras, apresentaremos sub tpicos que explicam de forma breve as etapas para tornar o cdigo eficaz e limpo. Nomes que fazem sentido Os nomes que fazem sentido so os nomes pronunciveis. A nomenclatura dada aos atributos, mtodos e classes deve ser autoexplicativa, ou seja, deve esclarecer sua real funo de imediato. Portanto, no economize nas palavras e evite abreviaes. Veja dois exemplos na Listagem 1. Listagem 1. Evite abreviaturas. No poupe as palavras. public class Aluno {
//SEM ABREVIAO O mais indicado private String cadastrarMatriculaDoAluno() { //implementao 3
} } Como indicado no exemplo, os nomes devem refletir exatamente o significado da funo do mtodo. Deste modo, defina-os de forma objetiva e clara, de modo que a leitura seja simplificada. Alm disso, evite as chamadas notaes hngaras, que ocorrem quando o tipo do objeto vem junto do nome (ex: listaDisso ou mapaDaquilo). A nomenclatura das classes deve ser elaborada a partir de substantivos e no deve conter verbos. Diferentemente dos nomes dos mtodos, que devem sim possuir verbos, pois representam uma determinada ao. Classes e mtodos Tanto as classes quanto os mtodos devem ter o menor tamanho possvel. Quanto menores eles forem, mais fcil se tornar o seu entendimento. No prprio livro Clean Code existem algumas normas voltadas aos mtodos informando que estes devem ter no mximo vinte linhas e cada linha de cdigo precisa ter no mximo cem caracteres, ao passo que a classe deve possuir entre duzentas e quinhentas linhas. No entanto, se o desenvolvedor tiver uma classe com 550 linhas, ou seja, se ele exceder 50 linhas, no preciso reescrever todo o seu cdigo. O fato de ter ultrapassado um pouco no significa que o profissional tenha que alterar toda a sua implementao para restringir-se s quinhentas linhas. Para isso existe o bom senso. Tambm importante que mtodos e classes tenham apenas uma funo. Esta norma chama-se princpio da responsabilidade nica ou, no ingls, Single Responsibility Principle. Dessa forma o entendimento torna-se muito mais rpido e preciso. Uma sugesto para detectar essa responsabilidade nica identificar se determinado trecho de cdigo est realizando uma funo diferente alm da proposta. O trecho de cdigo que realiza outra tarefa deve ser movido para um novo mtodo. Alm disso, busque evitar os mtodos que recebem parmetros demais. Mtodos com muitos parmetros acabam por se tornar confusos. A passagem de muitos parmetros indica justamente que o seu mtodo est realizando coisas demais. E se for este o caso, os mtodos devem possuir uma justificativa plausvel. importante entender que quando falamos em cdigo limpo, deve-se ter em mente a ideia de que Menos sempre Mais. Outra sugesto seguir a norma do DRY Dont Repeat Yourself, que no portugus significa No repita a si mesmo. Sendo assim, preste muita ateno repetio de cdigo. Evite a duplicidade reaproveitando os seus mtodos. Ademais, necessrio prestar ateno no grau de coeso das classes. Como informado anteriormente, menos pode significar mais. Dessa forma, nossas classes devem fazer apenas a funo descrita em seu nome. Um exemplo disso seria uma suposta classe Imprime, criada com o simples objetivo de imprimir. Portanto, ela deveria executar apenas esta funo, no precisando se preocupar em estabelecer conexo com o banco de dados, salvar os arquivos ou verificar se o usurio tem permisso de acesso. Quando uma classe possui mais responsabilidades, alm da que ela se prope, ela se mostra claramente incoerente, pois cada classe deve cumprir uma nica responsabilidade. Comentrios 4
Evite o mximo possvel fazer comentrios. Se partirmos do princpio explicado anteriormente, j podemos eliminar alguns tipos desnecessrios de comentrios, pois teremos seguido todas as regras para a nomenclatura de nossas classes e mtodos. Sendo assim, um comentrio com o propsito de explicar a funo de um mtodo se torna desnecessrio. No entanto, e se o projeto for um legado no qual se est realizando apenas uma manuteno, podemos inserir novos comentrios? De forma alguma. Devemos apenas verificar se os comentrios que j existem ainda fazem sentido em relao ao cdigo do mtodo. Caso contrrio, preciso alter-los, deixando-os coerentes com o cdigo novamente. Quando for necessrio realizar manutenes no cdigo de um projeto antigo, nunca se esquea de alterar tambm o comentrio do mtodo, caso eles existam. Ao lermos um comentrio, o correto seria no precisarmos olhar para o restante do cdigo para entend-lo. Caso precisemos voltar ao cdigo depois de ver o comentrio, ento este ltimo precisa ser alterado. Como diria o prprio Uncle Bob: Qualquer comentrio que faa voc olhar para outras partes do cdigo para entend-lo, no vale nem os bits que ele consome. Porm, h situaes em que os comentrios so necessrios. Eles podem ser teis em casos como, por exemplo, no aviso de consequncias que um trecho de cdigo possa vir a causar. Como uma lentido causada no banco de dados e a recomendao de que se faa uma alterao da procedure usada. Ou seja, o comentrio deve mostrar a real inteno por trs de uma deciso tomada, pois ocasionalmente fica difcil mostrar apenas com o nome do mtodo o porqu daquele cdigo ter sido feito de uma determinada maneira. Enfim, so raros os casos em que o comentrio pode vir a ser necessrio de fato. O ideal evitarmos a sua utilizao. Existem tambm os trechos de cdigo comentados. No entanto, um trecho de cdigo comentado deve ser algo deixado ali apenas por um breve momento, s servindo como uma rpida referncia. Se o mesmo j no tem nenhuma utilidade, apague-o sem medo, pois os controles de verso existem para isso, resgatar uma verso anterior para que possa ser novamente utilizada. Formatao A formatao do cdigo muito importante porque uma das formas como ns, desenvolvedores, nos comunicamos uns com os outros. A partir dela outros desenvolvedores podero olhar para o cdigo e entend-lo com mais facilidade. Ela , portanto, componente fundamental para a comunicao. A ordem dos mtodos outro fator essencial. Mtodos com conceitos relacionados devem ficar verticalmente prximos. Assim, criado um fluxo de leitura que melhora a legibilidade do cdigo. Outro fator importante o cuidado com a indentao do seu cdigo. Esta no deve possuir mais do que dois nveis, como o caso dos ifs mostrados na Listagem 2, que chegam a ter um terceiro nvel. Caso isso ocorra, deve-se extrair o terceiro nvel para outro mtodo. Listagem 2. Ateno indentao. Observe o erro comum na estruturao dos ifs. if(situacao == 1) { if(situacao == 2) { if(situacao == 3) {
} } 5
} preciso cuidado tambm com relao aos espaamentos dados no cdigo. O espaamento correto entre os operadores, parmetros e vrgulas fazem uma grande diferena no momento da leitura e da compreenso do que foi codificado. Podemos ver um simples exemplo das duas percepes na Listagem 3. Listagem 3. Observe o uso correto do espaamento. //SEM ESPAAMENTO public class CalculaDesconto {
public void caucula(Integer val1, Integer val2) { Integer resultado = val1 + val2; System.out.println(O resultado : + resultado); } } Tratamento de erros O tratamento de erros de suma responsabilidade do desenvolvedor. As coisas podem sempre dar errado e nosso dever garantir que o nosso cdigo possua o correto tratamento para cada situao. Neste contexto, importante que o desenvolvedor d prioridade ao uso de excees, em vez de apenas retornar cdigos de erro. O uso de cdigos de erro pode causar certa confuso, pois o mtodo que invocou determinada funcionalidade acaba tendo que se preocupar em tratar esses cdigos retornados e esse processo pode ser facilmente esquecido. As excees devem indicar a localizao exata de um erro. Portanto, ao lanar uma exceo, use mensagens que realmente informem o erro ocorrido, mencione o que de fato aconteceu, o que estava tentando fazer e o porqu do erro. Alm disso, evite usar excees para regras de negcio. Sempre use-as para os erros inesperados, como NumberFormatException. Para os tratamentos de erros de negcio, no devemos criar excees conforme o caso mostrado no exemplo da Listagem 4, pois so situaes especficas e difceis de serem previstas. Nestas situaes, devemos preferir o uso de ifs, pois torna o nosso cdigo mais organizado e de melhorar manuteno, como podemos constatar na Listagem 5. Listagem 4. Cuidado com o mau uso das excees. Despesas despesasComFestas = new Despesas(); try { despesasComFestas = despesasDao.getFestas(); resultado += despesasComFestas.getSoma(); } catch (DespesasComFestasNotFoundException e) { resultado += despesasComFestas.getSomaParcial(); } 6
Listagem 5. O correto uso de ifs para o tratamento de erros de negcio. Despesas despesasComFestas = new Despesas(); despesasComFestas = despesasDao.getFestas();
if(despesasComFestas.getSoma() != 0) { resultado += despesasComFestas.getSoma(); } else { resultado += despesasComFestas.getSomaParcial(); } Outro fato importante que tambm deve ser observado quanto ao tratamento de erros o de nunca retornamos null nos nossos mtodos, pois fatalmente poderemos causar uma exceo de NullPointerException. Tal exceo ocorre devido a um simples esquecimento, que seria o de tratar o trecho de cdigo com o if (qualquerObjeto != null). Mas o tratamento com o uso do if causa uma enorme duplicao de cdigo, devido grande quantidade de lugares nos quais teramos que repetir esse mesmo procedimento. Em vez disso, podemos optar por usar um pattern conhecido como Special Case Objects, que um padro responsvel por criar objetos especiais justamente para atender a essa necessidade. Esse pattern consiste na criao de um objeto do mesmo tipo da interface, com mtodos que retornem qualquer valor padro, como vazio para um atributo do tipo String e zero para um atributo do tipo Integer. Um exemplo pode ser visto no cenrio da Listagem 6. Nesta listagem uma classe denominada NullEmpregado, do mesmo tipo da interface Empregado, criada com mtodos que retornaro valores padres ao invs de retornar null. Listagem 6. Usando o pattern Special Case Object. public interface Empregado {
String nome(); String sobrenome();
}
//Nosso objeto especial public class NullEmpregado implements Empregado {
private static final String VALOR_PADRAO = ;
@Override public String getNome() { return VALOR_PADRAO; }
@Override public String getSobrenome() { return VALOR_PADRAO; }
}
public class Diretor implements Empregado {
@Override public String getNome() { return Pedro; 7
}
@Override public String getSobrenome() { return Otvio; } }
public class Funcionario implements Empregado {
@Override public String getNome() { return joao; }
@Override public String getSobrenome() { return Augusto; } }
//Nossa classe de testes public final class Escritorio {
Empregado empregado = new NullEmpregado();
public Escritorio() {}
public Escritorio(Empregado empregado) { this.empregado = empregado; }
public static void main(String[] args) { //Aqui o novo objeto no foi passado, o que nos retornaria NullPointerException, //mas com o uso do special case object isso no acontece. Escritorio escritorio = new Escritorio(); System.out.println(escritorio.imprimirNomeCompletoDoFuncionario());
escritorio = new Escritorio(new Funcionario());
escritorio = new Escritorio(new Diretor()); System.out.println(escritorio.imprimirNomeCompletoDoFuncionario()); } } Neste exemplo da classe Escritorio podemos ver que ela tambm foi instanciada tendo um construtor vazio. Isso certamente acarretaria em NullPointerException na execuo do mtodo imprimirNomeCompletoDoFuncionario(), pois o mesmo necessita de informaes oriundas de um Empregado. Contudo, essa exceo no ocorre porque Empregado j foi inicializado antes, com a classe NullEmpregado, garantindo assim o retorno de seus mtodos nome() e sobrenome() com o valor vazio, ao invs de null. Testes unitrios (TDD) Um Desenvolvedor que no faz testes como um cirurgio que no lava as mos (Robert C. Martin). Testes unitrios so os responsveis pela garantia do seu trabalho e da lgica que foi implementada no cdigo. 8
O TDD (Test Driven Development) uma das prticas mais conhecidas para a escrita de testes unitrios. Com ele, escrevemos nosso cdigo guiado pelos testes, ou seja, comeamos pelos testes da funcionalidade antes da sua codificao de fato. Assim, nosso cdigo j validado antes mesmo de sua escrita. Existem trs leis sobre o TDD, definidas por Uncle Bob, que servem como guia durante o processo de desenvolvimento. So elas: 1. No se pode comear a escrever o cdigo de produo at que se tenha criado um teste que apresente erro; ou seja, devemos sempre criar o teste primeiro; 2. No se pode escrever mais testes do que sejam necessrios para testar a condio do erro. preciso preocupar-se em criar o mnimo de testes, desenvolvendo apenas o suficiente para testar a condio de erro; 3. No se pode escrever mais cdigo de produo do que o suficiente para compilar o teste unitrio que apresentava erro. Robert C. Martin, no livro Clean Code, tambm recomenda o uso de cinco regras para realizao dos testes unitrios. A juno da primeira letra de cada um desses conselhos forma o acrnimo FIRST, que significa: Fast Os testes devem ser rpidos. Quando os testes so lentos, certamente o desenvolvedor no vai querer execut-los com frequncia. Deste modo, deixamos de encontrar os problemas logo no incio da codificao, o que permitiria solucion-los de forma mais rpida e fcil; Independent Os testes no devem depender uns dos outros, ou seja, o resultado de um teste no deve ser condio para a execuo do prximo teste. Eles precisam ser executados de forma independente e em qualquer ordem. Quando um teste depende do outro, o primeiro que falhar causar uma reao em cadeia, fazendo com que todos os outros testes abaixo na hierarquia produzam erro, dificultando diagnsticos e escondendo outros defeitos; Repeatable Os testes precisam estar aptos a serem executados repetidas vezes sem nenhuma interveno. Eles no podem depender de nenhum servio ou recurso externo que poder estar indisponvel em determinado momento. A execuo dos testes deve sempre apresentar o mesmo resultado, no importando o ambiente em que sua aplicao esteja rodando, seja no ambiente de desenvolvimento ou no ambiente de produo; Self-validating Os testes devem ter uma sada, uma resposta booleana, no importando se o retorno do mesmo indicou sucesso ou erro. No devemos depender de agentes externos para validar os nossos testes, como por exemplo, um arquivo de log. A resposta precisa ser imediata, ou verdadeiro ou falso; Timely O teste deve ser criado antes do cdigo da funo a ser testada. Quando optamos por criar vrias funes sem os devidos testes, fica muito mais difcil lembrar depois os testes que devem ser implementados. Alm disso, o desenvolvedor poder vir a optar por criar um super teste, abrangendo todas as regras dessas funes passadas, e isso certamente ir acarretar em uma lentido na hora de sua execuo. Outro problema a grande dificuldade na hora de realizar a manuteno nesses testes, pois cada vez que determinada funo inserida nele for alterada, precisaremos encontrar nesse super teste o ponto exato de verificao da funo em questo. 9
Por fim, importante ter em mente o fato de que, ao alterarmos um cdigo, devemos nos lembrar de tambm alterar o seu teste. Modelo Anmico x Modelo de Domnio Uma modelagem orientada a objetos visa ser uma representao fiel do mundo real. Essa representao se d atravs do uso de classes que devem ter seus estados e comportamentos bem definidos. Numa abordagem tcnica, dizemos que as classes (objetos) devem possuir propriedades, definindo o seu estado e mtodos, que implementam seu comportamento. Partindo da ideia do que a orientao a objetos, apresentaremos motivos para a no utilizao da modelagem anmica, visto que a mesma fere, por exemplo, o uso do encapsulamento no momento em que as suas classes de domnio so criadas apenas como um aglomerado de mtodos Getters e Setters sem uso real, servindo apenas como mtodos assessores para nossos atributos. Essas classes no possuem estado ou comportamento, indo justamente na contramo do que prega a orientao a objetos. Para a correta modelagem de nosso domnio, devemos considerar o uso do DDD (Domain-Driven Design), que prega a ideia de que classes de domnio devem possuir suas regras de negcio dentro delas mesmas, adquirindo assim estado e comportamento. Com isso, encapsulamos verdadeiramente a nossa implementao, podendo alter-la sem prejudicar outras classes que a utilizem. Os problemas com o uso do Modelo Anmico Quando pensamos em orientao a objetos, nos vem rapidamente a ideia de classes que representem o comportamento e estado de objetos do mundo real, como por exemplo, uma classe do tipo Carro, que possui o mtodo andar(). Outro conceito fundamental da orientao a objetos o do encapsulamento. Ele indica que no devemos expor os detalhes de implementao de nossos objetos. Isto , as demais classes no precisam saber como o procedimento de andar, mas apenas utilizar tal comportamento. Quando encapsulamos determinada implementao, podemos facilmente alter-la e com isso no interferir em outras classes, pois no existir outro cdigo que dependa diretamente desses detalhes. Ento, diante do que se sabe sobre uma boa modelagem de objetos, fato que toda nossa implementao deveria partir desses princpios, mas infelizmente no assim que acontece na prtica. Quando optamos pelo uso do modelo anmico, deixamos de lado as boas prticas da orientao a objetos porque os objetos do domnio nele criados no possuem qualquer comportamento. Assim, a nossa classe Carro deixa de ser a responsvel pelo comportamento de andar, passando essa funo para uma classe intermediria. Por esse motivo o modelo anmico hoje considerado um anti-pattern. Em um modelo desse tipo, getters e setters gerados a partir dos atributos declarados como privados dentro das classes de domnio no passam apenas de mtodos pblicos chamados de assessores, onde sua nica funo fornecer acesso a esses mesmos atributos privados. Na internet encontramos facilmente vasto material para iniciantes no desenvolvimento Java que disseminam o equivocado ensinamento de indicar que precisamos criar mtodos assessores para trabalhar com esses atributos, como podemos ver no exemplo da Listagem 7. Listagem 7. Criando os mtodos assessores, getters e setters no modelo anmico. 10
public class Salario {
private double liquido; private double bruto;
public double getLiquido() { return liquido; }
public void setLiquido(double liquido) { this.liquido = liquido; }
public double getBruto() { return bruto; }
public void setBruto(double bruto) { this.bruto = bruto; } } A classe Salario possui mtodos que ferem a ideia do encapsulamento, como por exemplo, o setLiquido(), que nos permite o acesso direto ao atributo liquido, alterando o seu valor a qualquer momento na implementao. Nesse caso no interessante que o nosso atributo seja alterado de forma direta, j que para se obter o salrio lquido, alguns clculos de desconto so necessrios, como o caso do INSS e o IRPF. importante no criar um getter ou um setter sem necessidade. Ao implementar qualquer mtodo em uma classe, a sua funo precisa ser clara e objetiva. Os getters e setters, quando criados, por diversas vezes no so invocados, e grande parte daqueles que esto sendo utilizados poderia ser substituda por mtodos de negcio do prprio domnio. Essa prtica de cri-los para todos os atributos declarados como privados nas classes de domnio vem do incio da AWT, quando era recomendado criar getters e setters para serem invocados no preenchimento dos campos visuais da interface grfica com o usurio. Essa prtica deu origem ao termo JavaBean, que uma classe com atributos privados que so acessados por mtodos pblicos. Quando se permite esse tipo de prtica, trechos como salario.setLiquido(salario.getBruto() (salario.getBruto() * 0,15)) sero encontrados ao longo de toda a aplicao. E se por acaso o clculo mudar, teremos que procur-lo e alter-lo por todo o nosso cdigo. Uma alternativa para contornar esse problema seria criar uma classe que ficasse responsvel pela lgica do clculo, como o exemplo da classe ContraCheque, que pode ser vista na Listagem 8. Listagem 8. Classe intermediria responsvel pelo negcio referente ao clculo do Salrio. public class ContraCheque {
public double calcularSalario(Salario salario, double desconto) { if(salario == null) { throw new InvalidArgumentException(salario no informado!); } if(desconto == null) { throw new InvalidArgumentException(desconto no informado!); } return salario.getBruto() (salario.getBruto() * desconto); } } 11
No entanto, essa soluo ainda mostra outros problemas, pois a classe ContraCheque apresenta-se de forma procedural, isto , possui validaes executadas em sequncia dentro do mesmo mtodo que realizar o clculo, ao invs de estarem em mtodos especficos. Lembre-se, devemos sempre separar as responsabilidades. E o que temos neste caso so procedimentos diferentes sendo realizados juntos (validaes e regra de negcio). Alm disso, a classe tambm possui uma forte ligao com a classe Salario, conhecendo demais a sua implementao. Isto se d a partir do momento em que a classe Salario disponibilizada como parmetro, dando assim acesso a todos os seus mtodos pblicos. Quando uma classe de domnio invocada dessa forma, a quebra do encapsulamento pode ser claramente notada, pois comportamentos e estados da classe passam a ser acessados de forma direta. O ideal nesse caso deixar de pedir em excesso o valor dos atributos, como faz o mtodo getBruto(), que serve apenas para trazer o valor do atributo bruto e a partir desse valor, realizar o clculo do salrio lquido. Portanto, importante definir claramente o que o objeto deve fazer, para que ele internamente seja responsvel pelas suas prprias regras de negcio (como por exemplo, pagar o salrio lquido). Este justamente o princpio do Tell, dont ask (Diga, no pea). Como este princpio no foi aplicado classe Salario, esta simplesmente parece no ter qualquer responsabilidade no sistema. Sem estado ou comportamento, ela se mostra como um fantoche, com mtodos sets e gets que servem apenas para fornecer e recuperar as informaes da classe. Para dar alguma ao ao domnio Salario, foi necessria outra classe, a ContraCheque. Devido a isso, qualquer alterao no domnio tambm ir acarretar modificaes na classe ContraCheque, porque ela conhece muitos detalhes da implementao de Salario. Na modelagem anmica dito que no devemos colocar qualquer lgica de negcio nos objetos de domnio. Contudo, o ideal exatamente o contrrio. Por que no deixar que cada objeto de domnio fique responsvel pelas suas prprias regras de negcio, unindo assim a lgica de negcio aos dados? Dessa forma eliminaramos os mtodos que apenas acessam e modificam diretamente os atributos. O fato que lgica de negcio e dados podem ser unidos de uma maneira simples, fazendo com que a classe de domnio passe a ter seus prprios mtodos de negcio, como mostrado na Listagem 9. Assim, tambm podemos remover os mtodos desnecessrios que apenas acessam e alteram o estado da classe de forma direta. Listagem 9. Atribuindo responsabilidade ao domnio Salario na viso do modelo de domnio. public class Salario {
return this.liquido; } } Como pode ser observado, o mtodo getLiquido() foi mantido, pois este faz parte do domnio e retorna o valor do salrio lquido j calculado. Quanto ao mtodo calcularSalario(), ele ficou responsvel apenas por realizar a regra de negcio. A validao dos atributos foi removida para outro mtodo, o validarAtributos(). Desse modo, temos as responsabilidades definidas de forma correta e qualquer alterao feita no clculo do salrio lquido, como por exemplo, a incluso de um novo desconto, no causar quaisquer alteraes numa outra classe que se utilize de getLiquido(). No momento em que a classe de domnio passa a ter seus prprios mtodos de negcio, ela deixa de ser uma mera estrutura de dados com Getters e Setters. Quando falamos em definio das responsabilidades, precisamos ressaltar que a mesma deve ser pertinente ao domnio. Tambm importante enfatizar que o comportamento dentro dos objetos de domnio no deve contrariar a ideia da separao em camadas do projeto, abordagem tradicionalmente pregada pelo MVC (Model-View-Controller). Sendo assim, domnio e lgica devem ficar restritos camada de modelo. O uso do DDD (Domain-Driven Design) Todo sistema nasce com o objetivo de resolver questes que envolvam determinado problema do mundo real. Para esse problema que ser resolvido com a implementao do nosso sistema, damos o nome de Domnio. Para uma correta modelagem do domnio devemos considerar o uso do DDD (Domain-Driven Design). Este, conforme a definio do site DDDComunity.org, no se trata de uma tecnologia ou uma metodologia, mas de uma maneira de definir prioridades, que objetiva acelerar o desenvolvimento dos projetos de software que lidam com domnios complexos. De acordo com o DDD, preciso que os desenvolvedores tenham um entendimento do domnio, no to profundo quanto o dos analistas de negcio (que so os especialistas do domnio), mas uma compreenso suficiente para que eles possam desenvolver guiados pelo domnio. E para que isso acontea, muito importante criar uma forma de comunicao entre todos os que iro lidar com o domnio. Essa conversa deve ocorrer entre os membros da equipe, se tornando o ponto chave para que uma linguagem comum se estabelea entre desenvolvedores e especialistas. Dessa forma, todos os membros da equipe iro criar, juntos, os termos ou vocbulos a serem utilizados. O conjunto de termos criados neste procedimento conhecido como Linguagem Ubqua. Definida uma terminologia comum e um entendimento das necessidades do domnio, os desenvolvedores j possuem um modelo a ser seguido para a implementao do cdigo, que deve ocorrer respeitando a 13
sequncia de desenvolvimento acertada nas conversas com os especialistas do domnio, item por item. Caso haja qualquer mudana no modelo, ela dever ser refletida no cdigo. Essa interao entre modelo e cdigo nos mostra que com o uso do DDD ocorre uma mudana de nfase. medida que o modelo mantido pelos analistas sofre alteraes, elas tambm precisam ser refletidas no cdigo mantido pelos desenvolvedores, tanto atravs da criao e remoo de funcionalidades, quanto atravs da refatorao. Para os desenvolvedores e analistas de negcio interessados numa abordagem mais profunda sobre DDD, recomendamos a leitura do livro Domain-Driven Design, de Eric Evans. Neste artigo, o nosso objetivo ao analisar as tcnicas de cdigo limpo de Martin Fowler, foi oferecer auxlio ao programador para que ele desenvolva melhor suas funcionalidades no cotidiano, com o mximo de clareza e eficincia. Dessa forma, quando este cdigo for manuseado por outro profissional, ele no ter dificuldades para continuar um trabalho que no foi iniciado por ele. Aliado s tcnicas de cdigo limpo, muito importante atentar para a escolha de como implementar o seu modelo de negcio. Conforme constatado, a escolha pelo modelo anmico fere as boas prticas do uso da orientao a objetos. Portanto, opte sempre pelo modelo de domnio. Por fim, recomendamos para a correta implementao do modelo de domnio, a prtica do DDD, que tem como ponto principal estabelecer uma forma de comunicao entre desenvolvedores e analistas de negcio, para que ambos possam caminhar juntos no ciclo de desenvolvimento. Links DDD Community Discusses sobre o assunto, diversos casos de estudo e exemplos do uso do DDD. http://dddcommunity.org Special Case Pattern, por Martin Fowler. http://martinfowler.com/eaaCatalog/specialCase.html Anemic Domain Model, por Martin Fowler. http://www.martinfowler.com/bliki/AnemicDomainModel.html Clean Code, escrito por Robert C. Martin. Domain-Driven Design, escrito por Eric Evans. Introduo Arquitetura e Design de Software, escrito por Paulo Silveira, Guilherme Silveira, Srgio Lopes, Guilherme Moreira, Nico Steppat e Fbio Kung.