6
30 www.mundoj.com.br os meados dos anos 90, fui apresentado à celebre obra “Design Patterns – Elements os Reusable Object-Oriented Software” de Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides (ou simplesmente Gang of Four – GoF), é até engraçado ressaltar que naquela época eu conhecia muito pouco sobre Orientação a Objetos e fiz muito pouco caso da obra. Para quem tem uma mente procedural Design Patterns são um grande mistério ou uma grande perda de tempo, tendendo bastante para a segunda opção. Nesse tempo, quase a totalidade do mercado (ao menos aqui no Brasil) ainda estava às voltas com programação procedural em linguagens como Clipper, COBOL, Visual Basic 3, FoxPro, Oracle Forms e muitas outras. Java ainda estava no berço, SmallTalk não era tão popular e a maioria dos programadores que trabalhavam com C++ comercialmente tinham pesadelos à noite. Hoje, me lembrando dessa fase da minha carreira, reconheço que a obra do Gang of Four foi bastante à frente do seu tempo e o mercado demorou para adotar esse “estilo” de desenvolvimento centrado em boas práticas de programação OO. Nessa linha de evolução do artigo, quero ressaltar que o Java é a linguagem que tem o mérito de popularizar a Orientação a Objetos e isso devemos muito a sua plataforma aberta e ao trabalho da comunidade. Não há dúvidas que o desenvolvimento de software é marcado pela enorme quantidade de proje- tos e empresas que adotaram o Java como plataforma padrão. O Java é um ambiente muito diversificado e a orientação a objetos atingiu sistemas Web, aplicações Desktop, sistemas Mobile, Middlewares, Banco de Dados e muito mais. Com o Java, padrões de design orientados a objetos começaram a ser mais divulgados e adotados por muitos programadores. Após 15 anos da publicação desta importante obra, que é base para todo e qualquer designer de software, muitos dos conceitos ainda são atuais e a maio- ria dos padrões são técnicas aplicadas pela maioria dos bons programadores todos os dias. O objetivo deste artigo é falar um pouco sobre como padrões do GOF são utilizados atualmente, dando bastante enfoque em padrões que são esquecidos pela maioria das equipes de desenvolvimento e que sofrem com a falta de aplicação de algumas técnicas importantes. Outras técnicas paralelas também serão citadas para facilitar o seu aprendizado. Patterns no Desenvolvimento de Software Moderno artigo Design de software é um assunto que exige estudo constante. Desde o lançamento da literatura de Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides, que despertou o assunto na comunidade, padrões, métodos e práticas se multiplicaram entre os desenvolvedores engajados em melhorar seus projetos. O artigo destaca a presença de Design Patterns dentro da evolução que vivemos no desenvolvimento de software moderno. Rodrigo Yoshima ([email protected]): Técnico em Processamento de Dados, bacharel em Administração de Empresas e autor. Escreveu artigos com renomados autores nacionais e internacionais como Scott Ambler, Jon Kern e James Shore. Trabalha com projetos de software há 16 anos, tendo treinado mais de 100 equipes em práticas ágeis no Brasil em diversas empresas e setores. Atua como arquiteto de Software e instrutor/consultor em práticas ágeis na Aspercom. N

Patterns no Desenvolvimento de Software Moderno · jeto web que são os mais comuns ao menos HTML, Javascript e algum dialeto de framework são utilizados. Ruby, C#, Python e muitas

Embed Size (px)

Citation preview

30 www.mundoj.com.br

os meados dos anos 90, fui apresentado à celebre obra “Design Patterns – Elements os Reusable Object-Oriented Software” de

Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides (ou simplesmente Gang of Four – GoF), é até engraçado ressaltar que naquela época eu conhecia muito pouco sobre Orientação a Objetos e fiz muito pouco caso da obra. Para quem tem uma mente procedural Design Patterns são um grande mistério ou uma grande perda de tempo, tendendo bastante para a segunda opção.

Nesse tempo, quase a totalidade do mercado (ao menos aqui no Brasil) ainda estava às voltas com programação procedural em linguagens como Clipper, COBOL, Visual Basic 3, FoxPro, Oracle Forms e muitas outras. Java ainda estava no berço, SmallTalk não era tão popular e a maioria dos programadores que trabalhavam com C++ comercialmente tinham pesadelos à noite. Hoje, me lembrando dessa fase da minha carreira, reconheço que a obra do Gang of Four foi bastante à frente do seu tempo e o mercado demorou para adotar esse “estilo” de desenvolvimento centrado em boas práticas de programação OO.

Nessa linha de evolução do artigo, quero ressaltar que o Java é a linguagem

que tem o mérito de popularizar a Orientação a Objetos e isso devemos muito a sua plataforma aberta e ao trabalho da comunidade. Não há dúvidas que o desenvolvimento de software é marcado pela enorme quantidade de proje-tos e empresas que adotaram o Java como plataforma padrão. O Java é um ambiente muito diversificado e a orientação a objetos atingiu sistemas Web, aplicações Desktop, sistemas Mobile, Middlewares, Banco de Dados e muito mais. Com o Java, padrões de design orientados a objetos começaram a ser mais divulgados e adotados por muitos programadores.

Após 15 anos da publicação desta importante obra, que é base para todo e qualquer designer de software, muitos dos conceitos ainda são atuais e a maio-ria dos padrões são técnicas aplicadas pela maioria dos bons programadores todos os dias. O objetivo deste artigo é falar um pouco sobre como padrões do GOF são utilizados atualmente, dando bastante enfoque em padrões que são esquecidos pela maioria das equipes de desenvolvimento e que sofrem com a falta de aplicação de algumas técnicas importantes. Outras técnicas paralelas também serão citadas para facilitar o seu aprendizado.

Patterns no Desenvolvimento

de Software Moderno

a r t i g o

Design de software é um assunto que exige estudo constante.

Desde o lançamento da literatura de Erich Gamma, Richard

Helm, Ralph Johnson e John Vlissides, que despertou o assunto

na comunidade, padrões, métodos e práticas se multiplicaram

entre os desenvolvedores engajados em melhorar seus

projetos. O artigo destaca a presença de Design Patterns dentro

da evolução que vivemos no desenvolvimento de software

moderno.

Rodrigo Yoshima

([email protected]): Técnico em

Processamento de Dados, bacharel em

Administração de Empresas e autor. Escreveu

artigos com renomados autores nacionais e

internacionais como Scott Ambler, Jon Kern

e James Shore. Trabalha com projetos de

software há 16 anos, tendo treinado mais

de 100 equipes em práticas ágeis no Brasil

em diversas empresas e setores. Atua como

arquiteto de Software e instrutor/consultor em

práticas ágeis na Aspercom.

N

31

Bons designs requerem experiência Simples dependências!

Sempre que nos é apresentado algum conceito novo, ou alguma técni-

ca nova, ou até uma nova ferramenta temos um pensamento do tipo:

“Como é que vou aprender isso?”. Como exemplo, o desenvolvimento de

software atual é poliglota. Não basta aprender Java. Dificilmente você

terá projetos em que é utilizada uma única linguagem. Em qualquer pro-

jeto web que são os mais comuns ao menos HTML, Javascript e algum

dialeto de framework são utilizados. Ruby, C#, Python e muitas outras

estão sendo adotadas na maioria das empresas. No mundo atual da

computação em nuvem (cloud computing), o espectro de linguagens,

paradigmas e técnicas estão mudando rapidamente a maneira como nós

programamos, testamos e integramos nossas aplicações. Aprender bem

o que são e como são aplicadas essas novas práticas vem ditando o ritmo

do desenvolvimento de software moderno. Além de diferentes sintaxes

e filosofias de linguagens e plataformas, práticas ágeis também estão

“tomando a cena”. Está cada vez mais comum empresas adotarem design

incremental, desenvolvimento orientado a testes (TDD), programação

em par, builds rápidos e integração contínua.

Fazer um design qualquer não é difícil. É capaz que com um treinamento

e uma IDE nas mãos você consiga fazer um sistema usando “coisas” da

orientação a objetos com uma qualidade até aceitável. Porém, desenhar

um software realmente orientado a objetos é difícil. Fazer um sistema

orientado a objetos e visando reuso é ainda mais difícil. Fazer um sistema

grande, que visa reuso e que é fácil de manter e evoluir pode ser até uma

missão impossível.

Em qualquer um dos cenários difíceis citados, o conhecimento e apli-

cação de boas práticas de programação é mandatória, e são poucos os

desenvolvedores que acertam com elas. Como consultor, tenho visitado

diversas empresas de norte a sul do Brasil, e tenho verificado que a maio-

ria das empresas tem problemas de design. Não é muito difícil encontrar

aplicações que foram desenvolvidas de maneira demasiadamente des-

preocupada com o design e que atualmente geram muito custo para o

bolso dos empresários (e contribuintes, no caso de sistemas no governo).

Um grande problema de aprendizado sobre design de software é que

apesar de existir inúmeros livros, blogs, artigos e exemplos sobre o as-

sunto, projetar soluções de software vencedoras só vem com o tempo e a

experiência. O conhecimento teórico é muito bom, mas é limitado. Eu só

vejo que você se tornará um bom designer de software após literalmente

“bater cabeça” em dois ou três projetos grandes. Projetos diferentes

possuem problemas diferentes de design e técnicas também diferencia-

das. Nunca um software é igual a outro, e ao mesmo tempo que isso é

bastante desafiador na nossa área também pode ser bastante frustrante

em algumas ocasiões.

Design ruim é um câncer difícil de ser curado. Se a sua aplicação cresceu

de maneira desorganizada, sem boas práticas e principalmente sem

testes automatizados, rapidamente ela se tornará um “monstro” mono-

lítico difícil de manter e evoluir. É muito comum sistemas se tornarem

legados custosos logo que “saem do forno” pelo simples fato das equipes

desconhecerem boas práticas de Design. Vamos ver algumas dessas boas

práticas e também vamos revisitar alguns padrões.

O Java, além de popularizar orientação a objetos, também abriu muitas discussões sobre o assunto. Infelizmente essa discussão fica na esfera técnica e poucos empresários ou gerentes sabem o valor da orientação a objetos como vantagem competitiva e também sua importância para o negócio. De qualquer forma, orientação a objetos é popular, um pa-radigma adotado pelas massas, entretanto, não necessariamente bem compreendido pelos seus usuários.

Para formar uma base para conceitos mais avançados, vamos dar um passo atrás e falar sobre algo bem pouco compreendido que são Depen-dências. Se você conhece bem sobre a Lei de Demeter e Separação de Conceitos fique à vontade para simplesmente pular essa parte.

Muito se fala que as medidas da qualidade do código são alta coesão e baixo acoplamento. Para analisar acoplamentos é necessário compreen-der dependências. Uma dependência é tudo que um determinado obje-to precisa para funcionar, seja estruturalmente ou semanticamente. Se é necessário enviar mensagens diretamente para um determinado objeto, é estabelecida uma dependência – e para a orientação a objetos, um ob-jeto chamando um método em outro objeto é um envio de mensagem. Se um objeto carrega outro objeto através de uma associação também é uma dependência. Muito dos design patterns são para remover depen-dências desnecessárias ou torná-las mais leves.

Imagine que você está implementando um sistema de logística e precisa calcular o frete de um determinado pedido de vendas. Veja a Listagem 1.

Listagem 1. CalculoFrete.java.

public class CalculoFrete {

private PedidoVendas pedido;

public CalculoFrete (PedidoVendas pedido) {

this.pedido = pedido;

}

public BigDecimal obterPesoPedido() {

BigDecimal pesoTotal = new BigDecimal(0D);

for (ItemPedido item : pedido.getItens()) {

pesoTotal = pesoTotal.add(item.getProduto().getPeso().

multiply(item.getQuantidade()));

}

return pesoTotal;

}

}

O que você consegue ver de errado no código da Listagem 1? Preste atenção no loop que calcula o peso de cada item. Você consegue ver que há muitas dependências? Eu estou iterando na lista de itens, pegando o peso do produto e multiplicando pela quantidade. Você consegue ver

32 www.mundoj.com.br

Figura 1. Dependências explícitas do CalculoFrete.

Um diagrama UML pode deixar mais clara as dependências. Neste caso, o CalculoFrete está violando a Lei de Deméter que diz para você “só conver-sar com os seus amigos”. A ideia é somente enviar mensagens e depender somente das classes diretamente associadas e não seus membros.

Muitos de vocês devem estar pensando que esse conceito é bastante básico, porém, infelizmente, é no básico que eu vejo a maioria das equi-pes errando. Além da violação da Lei de Demeter, essa implementação do CalculoFrete também viola a Separação de Conceitos, outro princípio para um bom design. A Separação de Conceitos (Separation of Concerns) nos diz que todo elemento deve ter uma única responsabilidade, isso nos leva a ter sempre classes bem pequenas e focadas em resolver somente um único problema. É correto pensar que para CalcularFrete eu preciso do peso, porém, minha classe é focada em calcular frete e não em descobrir o peso dos produtos vendidos. Naturalmente você pode pensar que essa responsabilidade é do próprio PedidoVendas, entretanto, se você tomar a decisão que o pedido não deveria saber seu próprio peso você poderia criar uma nova classe para resolver esse problema. Veja a figura 2.

Figura 2. CalcularPeso como uma responsabilidade separada.

Na figura 2, simplesmente separamos conceitos. A classe CalcularPeso neste caso pode atuar como uma estratégia do CalculoFrete (Strategy), pode ser um Mediator, um Wrapper ou até uma mistura deles. O modelo UML não me diz muita coisa, mas o que irá definir sobre como eles ele-mentos irão se comportar será definido pela forma como eles interagem, quem inicia a ação e muitas outras coisas. Padrões não são a motivação do seu design, mas sim as consequências de um design melhorado. Nes-se exemplo, eu não pensei em implementar um dos padrões citados, mas sim em desacoplar o cálculo de peso.

Separação de conceitos não se aplicam somente a classes e objetos. Há outros níveis no seu projeto nos quais é igualmente importante ter foco em resolver somente um único problema, fazendo outros elementos colaborar quando for necessário.

Um dos grandes problemas que empresas sofrem são aplicações legadas e monolíticas, fruto de más práticas de programação. Esses sistemas são construídos ao longo do tempo de maneira desorganizada e que atual-mente são difíceis de manter. Muitas vezes estão utilizando tecnologias obsoletas, não possuem qualquer tipo de teste automatizado e todo o conhecimento do funcionamento interno do sistema está na cabeça de alguns programadores. Sem dúvidas uma aplicação dessas é um ralo de dinheiro. Segundo pesquisas recentes, 80% do investimento em sistemas é direcionado a encontrar e corrigir bugs, e infelizmente, as más práticas continuam proliferando no nosso mercado.

Se você fizer uma engenharia reversa do seu projeto atual para uma ferra-menta UML como seu modelo se parecerá? Caso ele fique parecido com a figura 3 você está com problemas! Para fugir dos “monstros monolíticos” existem técnicas para organizar e simplificar seus modelos. Muitas pessoas usam Separação de Conceitos nas camadas da aplicação. Camadas são am-plamente difundidas e discutidas, porém, quebrar Partições é bem pouco discutido e poucas equipes separam módulos de seus projetos reduzindo dependências e buscando maior simplicidade.Veja a figura 4.

Figura 4. Camadas x Partições.

Simplificar algo complexo é organizar elementos hierarquicamente. Uma empresa pode ter muitos sistemas, sistemas são compostos por módu-los, módulos possuem componentes e componentes agrupam classes. Por sua vez, classes são atributos e operações. Entre todos esses níveis,

Separação de conceitos, camadas, partições

que a classe CalculoFrete está “invadindo a privacidade” do PedidoVen-das? Muitas vezes quando escrevemos código é comum pensarmos so-mente no que é necessário implementar naquele momento, e acabamos tomando a responsabilidade de outras classes na implementação da classe corrente. Veja a figura 1.

É importante ressaltar que um código altamente acoplado pode ter menos linhas e menos elementos do que um código bem modelado. Um design acoplado pode parecer mais simples à primeira vista. Já um bom design pode ser mais verboso e com mais elementos, porém, será mais fácil de manter e de evoluir se comparado com um design acoplado. Muitos programadores atualmente sentiram o forte “puxão de orelhas” do Martin Fowler sobre modelos anêmicos (ver referências) e agora estão caindo na outra armadilha do alto acoplamento violando a Separação de Conceitos.

Como alertado, esses conceitos são bastante básicos e muitos progra-madores já conhecem e aplicam essas técnicas no nível das classes do seu projeto, porém, Separação de Conceitos e reduzir dependências também se aplicam em outros níveis de organização que muitas vezes são esquecidos.

33

combater dependências pode simplificar o seu design. Não entenda hierarquia aqui como herança da orientação a objetos, mas sim como níveis de granularidade diferentes.

As camadas separam conceitos segundo sua função na arquitetura: te-mos telas, elementos do domínio (camada de negócios), componentes de infra e não é uma boa ideia misturar esses conceitos todos na mesma implementação. A ideia é que cada uma dessas responsabilidades per-tençam à sua camada, mas não só isso: as camadas organizam o reuso. Elementos de infraestrutura e de negócio são amplamente reutilizados na organização, enquanto código de aplicação e view raramente são reutilizados [Jacobson]. Decisões de design devem visar a simplicidade: usamos camadas para não deixar que complexidades acidentais da nossa arquitetura poluam principalmente a nossa camada de negócios. Apesar desse conceito ser bastante antigo, está sendo reforçado atu-almente pela Domain-Driven Design – a camada de negócios reflete o negócio, seus dados, comportamentos e associações. Os padrões do GOF são genéricos e encontrados em todas as camadas e, além disso, usamos alguns deles para integrar sistemas e separar Partições.

As Partições foram propostas por Grady Booch como uma separação ortogonal às suas camadas, cortando-as perpendicularmente e isolando a execução de objetos. As partições são funcionais e comumente são chamadas de módulos, porém, há uma grande confusão sobre módulos, empacotamento, componentes etc. De qualquer maneira, independen-temente do termo, seu projeto grande deve ter separações funcionais e fronteiras explícitas que auxiliem a simplificar seu design.

O maior uso das partições é tornar esses agrupamentos de classes ou componentes mais claros, separando fronteiras e simplificando o seu de-sign. Isso tornará as dependências entre elementos melhor gerenciadas. É bastante comum as pessoas acharem que o simples empacotamento das classes (packages do Java ou namespaces do C#) já é suficiente para particionar e modularizar uma aplicação. Porém, simplesmente separar packages funcionalmente não resolve a questão.

Veja a figura 5. Na construção dessa aplicação, o desenvolvedor até teve o cuidado de empacotar classes funcionalmente, porém, veja que os pa-cotes se tornam bastante inúteis nessa abordagem para separar concei-tos e gerenciar dependências. Mesmo usando pacotes, seu sistema ficará

Só usar Packages não ajuda... Quando modularizamos a aplicação (Partitions [Booch], Modules [Evans] e Façade [GOF]), buscamos agrupar componentes hierarquicamente tentando ao máximo reduzir as dependências e tornando-as explicitas em se tratando de outros módulos. Com isso, aplicamos Separação de Conceitos, reduzimos o acoplamento e aumentamos a coesão no nível dos módulos. Algumas pessoas podem chegar à conclusão que fazemos isso para poder “trocar” o módulo de estoque por alguma outra imple-mentação. Apesar dessa opção de Design tornar isso possível, essa não é a maior motivação dos módulos e particularmente nunca vi isso acon-tecer na prática!

Usamos módulos para melhorar a visibilidade do nosso design, tornando grupos de funcionalidades mais coesas, fáceis de gerenciar e testar. O isolamento de módulos permitirá avaliar impactos das alterações no mó-

monolítico como o exposto na figura 3. Se todo mundo conversar com todo mundo, o modelo é caótico e crescerá de maneira desorganizada. O padrão Façade [GOF] é uma boa opção para resolver esse problema e poucas vezes é utilizado com essa finalidade. Veja a figura 6.

34 www.mundoj.com.br

Design de software é um assunto bastante amplo e variado. Todos os dias temos desenvolvedores de software construindo aplicações diferentes, tomando decisões, lidando com interfaces, classes, métodos e toda a parafernália que nossas linguagens de programação oferecem. Muitas vezes, entre comunidades de linguagens diferentes pode haver uma visão completamente diferente sobre os patterns catalogados, seus usos e suas implementações. É interessante verificar que atualmente há um contato maior entre essas comunidades. Neste ano de 2009 tivemos ao menos três eventos sobre desenvolvimento de software aqui no Brasil que não eram direcionados somente a uma única plataforma ou linguagem.

Essa maior interfertilização entre comunidades é o simples reflexo da mudança que tem ocorrido na postura das empresas e envolve muito o design de software. Há 15 anos, bem no início da minha carreira, havia “disputa” entre duas ou três linguagens (e suas comunidades) para saber qual era a melhor. Havia uma luta frenética para ter uma “uniformidade” dentro da TI: aqui é só entra Oracle, ou aqui só entra Microsoft era a or-dem da maioria dos CIOs (apesar desse nome não existir na época). Todos buscavam uniformidade nos bancos de dados, plataformas, arquiteturas, runtimes, linguagens e tudo mais. Esse mito da “uniformidade” está caindo nos últimos anos. As empresas estão tendo que lidar com fusões, aquisições, novos produtos, adaptações a diferentes mercados, negócios globais e muito disso envolve a TI. Essa uniformização passa a não fazer mais sentido. A TI está tendo que lidar com essa pluralização de serviços, linguagens e ambientes e assim, chegamos a aplicações orientadas a serviços e a processos de negócio (SOA).

Se isso está ocorrendo nas decisões do futuro das empresas, isso tam-bém influencia no nosso trabalho no dia-a-dia do desenvolvimento de software. No início da plataforma Java, há uns 10 anos, a falta de abs-

tração da infraestrutura nos obrigava a implementar praticamente tudo (desde servidores TCP até o esquema de persistência). As abstrações eram pobres, o design era pesado, trabalhoso e bem pouco divertido. Nem preciso dizer que os projetos demoravam a ser entregues e muitas das decisões de design eram sobre a infraestrutura. Era bastante comum implementar o padrão Command [GOF] para lidar com requisições HTTP (como o Struts 1 trabalha). Proxies/Mementos [GOF] e mais um conjunto de classes e interfaces eram necessárias para implementar Lazy Load, algo que é bastante banal nos frameworks de persistência de objetos mais atuais (como o Hibernate). Muitos desses padrões atualmente já não são tão utilizados. Essa é uma boa prova que boas abstrações mu-dam e simplificam o design.

Voltando uns 5 a 7 anos, a infraestrutura melhorou, surgiram frameworks e outras alternativas, porém, ao mesmo tempo, entrou na moda uma arquitetura megalomaníaca. A preocupação com escalabilidade cegou a maioria dos designers, aliado a uma adoção inadvertida da arquitetura referência da Sun: objetos distribuídos pareciam ser uma boa ideia, e sur-giram várias implementações e referências do catálogo da Sun. Mesmo sistemas medianos e muitas vezes simples sistemas departamentais ti-nham EJBs “distribuídos”. Nem preciso dizer que isso era mais um exagero do mercado. As palavras da moda eram interfaces remotas, RMI, CMP/BMP, arquitetura padrão, uso errado da UML, frameworks caseiros gigan-tescos, geração de código de infraestrutura, MDA e muitas outras coisas. Nessa época, pesquisas revelaram que a cada 10 linhas de código Java somente três eram estritamente relacionadas com o negócio e as outras sete eram puramente burocracias da arquitetura ou dos frameworks vigentes.

Voltando aos eixos do equilíbrio, a mentalidade dos desenvolvedores foram muito influenciadas pelas práticas ágeis de desenvolvimento de software, destacando o Extreme Programming (XP). A simplicidade é um dos valores do XP. O objetivo da simplicidade é maximizar a quantidade de trabalho NÃO FEITO. Nós não somos pagos para configurar pool de conexões com o banco de dados, não somos pagos para implementar frameworks, não somos pagos para escrever documentos e nem mesmo para escrever código. Nós somos pagos para resolver problemas de ne-gócio dos nossos clientes.

Nessa linha, desenvolvimento iterativo, design incremental e desenvol-vimento orientado a testes passaram a se tornar o paradigma atual para design de software. As arquiteturas se simplificaram bastante, mas mes-mo assim têm o poder para escalar. Primeiramente, o sentimento atual com relação a design de software é pragmático. Um dos lemas do próprio XP é “você não vai precisar disso”! Faça um design simples, e evolua con-forme os requisitos. Não tente prever o futuro e não polua o seu design com especulações do tipo “e se o usuário me pedir mais isso lá na frente?”.

Para os leitores mais experientes, talvez sua a história tenha sido um pouco diferente. Seja qual for a sua história, muito pragmatismo, muitas decisões em equipe e muitos testes automatizados é o que rege o design de software atual dentro das práticas modernas de desenvolvimento. Destaco que design patterns, principalmente os do GOF, são usados in-tensamente no design incremental. Como modelamos a aplicação sem-pre pensando em colaboração e responsabilidades dos objetos em face a refatorações futuras o sistema deve se basear num excelente design OO. Uma das coisas que nos ajudam a melhorar o design é o desenvolvimen-to orientado a testes.

dulo Vendas no módulo Estoque de uma maneira mais clara, facilitará o Build e Deployment e fará os testes automatizados rodarem mais rápido. Esta é uma excelente estratégia para fugir dos “monstros monolíticos” que assolam nossas empresas.

Esse assunto é bastante amplo e existem outras estratégias e patterns para integrar partições/módulos. Uma pergunta comum é: “-De onde surgem os módulos?”. É interessante que na maioria das vezes o seu cliente ou especialista do domínio já saiba quais são os módulos. É natural que a mente humana pense a respeito de determinado assunto complexo em partes menores separadas, mesmo que seja uma pessoa de negócio e não um programador. Os módulos não são divisões técnicas e sim relacionadas com o negócio, seguindo aquilo que a Domain-Driven Design chama de Linguagem Ubiqua (ver o artigo “Domain-Driven De-sign para um Mundo Real”, MJ 28).

É importante ressaltar que é saudável para o seu projeto pensar no dese-nho inicial dos pacotes antes de começar a implementação das funciona-lidades. O Eric Evans, no seu renomado livro sobre Domain-Driven Design (veja referências), faz um importante alerta: é muito difícil refatorar mó-dulos. Assim sendo, uma conversa de 30 minutos a cada iteração entre todos os membros da sua equipe para discutir as fronteiras entre módu-los é altamente recomendado. Veja que o que estou recomendando aqui é uma conversa rápida, e não uma fase de design de meses. Isso não é uma apologia ao processo de desenvolvimento em Cascata. Bom senso é a base para uma equipe de desenvolvimento de software vencedora.

35

O design moderno visa a testabilidade Conclusão

A maior diferença que destacamos sobre a orientação a objetos comparada com o paradigma procedural é o encapsulamento. Ao contrário do que tínha-mos no modelo procedural, um design orientado a objetos não é um conjunto de algoritmos encadeados no estilo “entrada-processamento-saída”. Objetos possuem estado e trocam estímulos através de mensagens. As mensagens que o objeto responde fazem parte da sua interface pública. Como disse, uma mensagem pode ser uma simples chamada de método, assim como um estímulo remoto, assíncrono que veio de uma fila que você nem sabe qual é. Objetos possuem encapsulamento forte, e os objetivos do sistema são cum-pridos com a colaboração entre eles através da troca de mensagens.

Dado esta característica dos objetos, podemos facilmente testar sua imple-mentação através de testes de unidade. Eu lembro muito bem quando meu professor de lógica de programação falou que um compilador só consegue detectar erros de sintaxe do meu programa, mas não erros de lógica. “A sintaxe pode ser checada pelo computador, mas não a lógica”, dizia ele. Eu creio que atualmente nós podemos desafiar esse conceito usando testes automatizados e práticas de desenvolvimento orientado a testes (TDD). É possível validarmos a lógica de um programa assim como o compilador (ou interpretador) faz com a sintaxe.

É uma premissa fundamental que os objetos tenham somente uma respon-sabilidade, assim como é importante que eles tenham um encapsulamento que proteja seu estado. Se um objeto tem uma interface pública e esta pode ser utilizada por qualquer outro componente, temos todo ferramental para concentrar testes nessa interface e, assim, conseguimos validar deterministi-camente se a lógica do componente funciona.

Além disso, se escrevermos os testes antes da implementação teremos uma descrição muito clara da interface necessária. Nosso componente será simples, pois atenderá somente aquilo que nosso teste exige. Desenvolver a aplicação orientada a teste (TDD) é uma das melhores ferramentas para ter um bom design, além de nos dar segurança para evoluirmos o design refa-torando sempre que necessário. Ter bons testes tira o nosso medo em mexer naquelas partes obscuras do código. Refatoração só existe de forma segura com testes.

Muitos desenvolvedores questionam como testes podem melhorar o design. Ora, a resposta é bem simples, e está relacionada com os conceitos defendidos neste artigo como coesão, acoplamento e dependências: se um design a prio-ri é difícil de testar isoladamente (com testes unitários) consequentemente é um design acoplado e difícil de manter. A facilidade de testar os componentes é proporcional à qualidade do seu design.

É muito interessante que o Erich Gama na entrevista desta edição mencionou que a Injeção de Dependências (DI) seria um padrão digno de entrar na obra do GOF. As soluções atuais de DI garantem uma melhor testabilidade, pois todas as ligações são feitas externamente à inteligência do próprio compo-nente, permitindo o uso de Mocks e Stubs para simular o comportamento dessas dependências durante um teste de unidade.

Tenho discutido muito sobre design de software com várias pessoas e vejo que a falta de testes é uma das piores faltas em design. É melhor ter um de-sign ruim com testes do que ter um design bom sem testes. O design ruim com testes você pode com segurança refatorar para que ele se torne um bom design. Com um design bom sem testes você não tem nada! Concluindo, um bom design inclui testes.

É bem provável que devido aos “ventos” do modismo em TI muitos que leram o artigo até aqui podem reclamar a falta de uma ênfase maior em SOA. Meu objetivo aqui foi focar mais em práticas da orientação a obje-tos em design de classes, componentes e módulos. Sou bastante realista com SOA, e vejo que relacionada com processos de negócio, ela é uma excelente abordagem para integração entre sistemas, sempre tomando MUITO cuidado com a granularidade dos serviços (veja o artigo do Jim Weber nas referências).

Design Patterns são fundamentados em bons princípios como os apresentados aqui. Assim como é importante o estudo dos Patterns é igualmente importante estudar sobre seus fundamentos. Separação de Conceitos, Lei de Deméter, Responsabilidade Única do Objeto, Depen-dências são somente alguns deles.

Sobre Partições, destaco que esta é uma prática bastante esquecida e deveria ser utilizada pela maioria das grandes aplicações empresariais. A melhor medida da qualidade do seu design é o custo de manutenção das suas aplicações. Outra medida é o seu entusiasmo em programar: você gosta de trabalhar com seu código?

Por fim, desenvolvimento orientado a testes é uma excelente ferramenta para melhorar o seu design. O grande problema com o TDD é a mudança comportamental no desenvolvedor. Pensar em testes primeiro é uma mudança significativa na maneira que você programa. Uma excelente forma de aprender esta prática é pareando com alguém que sabe ou

Referências

-

-

siness Success

-

cDomainModel.html