domingo, 23 de junho de 2013

Boas práticas comportamentais do desenvolvedor de software

Durante os meus 11 anos trabalhando com desenvolvimento de software, pude perceber diversos comportamentos em nossa área que devemos evitar, a fim de não cairmos em fatores como problemas de relacionamento com os colegas de time, queda de produtividade e qualidade durante os processos de desenvolvimento de software. Desta forma, preparei 11 conselhos - um para cada ano. Dizem que, se conselho fosse bom, a gente não dava, vendia. Porém, as práticas comportamentais a seguir têm funcionado bem para mim e para os colegas de profissão que observo praticá-las. Espero que funcionem bem para você, também. Boa leitura!

1) Seja construtivo: ao ver um erro, trecho de código ou até uma arquitetura que você não concorde, não condene o trabalho feito por outros colegas. Se você considera ter um conhecimento de causa melhor, então dê não somente dicas, mas também mostre um exemplo prático, em forma de código, de como o trabalho realizado pode ficar melhor. Ficar jogando pedras no telhado do vizinho fará somente com que você passe uma imagem de alguém intragável, que só quer aparecer às custas dos outros, além de afastar seus colegas. Tenha em mente que o mesmo insulto que você faz aos outros de alguma forma se voltará contra você.

2) Respeite os novatos: você não nasceu sabendo tudo. Já passou pela mesma curva de aprendizado que outros profissionais que acabaram de entrar no mercado estão passando. Então não desanime seu colega novato quando perceber que ele ainda não possui níveis de maturidade em análise e código como os seus. Ajude-o a progredir, em vez de traumatizá-lo. Ele será eternamente grato a você por isso. Pode ter certeza que, como o mundo dá voltas, ele se lembrará de você caso em algum momento da vida ele esteja por cima e, você, precisando de um emprego. Se ele terá boas ou más recordações de você, só dependerá de suas atitudes para com ele no presente.

3) Respeite os antigos: sabe aquele profissional que trabalha na empresa desde longa data e faz manutenção de um software que possui tecnologia antiga que será migrada para uma tecnologia nova para a qual você foi contratado? Não subestime-o. Não pense que ele é o obsoleto da história e que você está na crista da onda. Ele possui um conhecimento que você simplesmente está longe de dominar: os processos e o negócio da empresa. Seja muito legal com ele, escute todas as suas considerações, absorva ao máximo sua experiência e seus conselhos e tenha-o como o seu tutor, não como o seu concorrente. Não tente pular os degraus à força. Até porque este cara que você menospreza e considera obsoleto pode se atualizar e aprender as técnicas modernas que você preza tanto no máximo em 12 meses, enquanto você ainda terá que comer muito arroz com feijão para entender todo o negócio da empresa, os meandros dos componentes, das dlls antigas registradas e do banco de dados.

4) Não (des)arrume o código demais: se você foi solicitado a fazer uma manutenção corretiva ou evolutiva no software, procure os pontos onde o código deve ser mudado. Não dê uma de herói, querendo mudar o código inteiro, a não ser que você possua um domínio pleno daquela parte específica do software e tenha tempo sobrando para fazer a mudança. Você não sabe o porquê de alguns métodos existirem. Querer apagá-los ou mudá-los completamente será como dar um tiro no pé, pois você gastará mais tempo que o necessário e poderá fazer com que erros que não ocorriam antes da manutenção passem a ocorrer.

5) Não pire na tecnologia: o mercado de desenvolvimento de software irá sempre trazer novidades que julga serem "boas práticas". Antes de querer sair aplicando tudo o que é novo, faça a si próprio as seguintes perguntas:
- A arquitetura de software que utilizamos na empresa já possui uma organização e performance adequadas?
- Estou querendo aplicar esta novidade por necessidade? Ou por pura vaidade?
Pense muito bem e, antes de aplicar novidades, sugira, demonstre com exemplos práticos que é benéfico aplicá-las. Não saia fazendo as coisas ao seu bel prazer. Até porque, depois que você fez a mudança, tudo estará muito claro para você, mas o resto do time irá penar para entender. Um dia você sairá da empresa e seus colegas devem ter condições de entender o código que você fez. Tenha o bom senso de aplicar tão somente as novidades que a empresa e seus companheiros de time precisam para o desenvolvimento de uma arquitetura simples, clara, coesa, fortemente orientada a reúso e com facilidade de manutenção do código. Quem gosta de complicar e não tem o pensamento de desenvolver para o time, não está sendo inteligente. Está sendo, no máximo, um "intellisense nonsense". Não seja egoísta, pense como time!

6) Não empaque: muitas vezes perdemos muito tempo tentando decifrar questões técnicas ou de negócio durante o desenvolvimento ou manutenção de software, enquanto um colega que está do nosso lado poderia "encurtar o sofrimento" e nos auxiliar, pois já solucionou algum problema parecido. Se você perceber que o tempo está passando e que não está conseguindo chegar a uma solução, não tenha vergonha de perguntar a um colega. É claro, a empresa deve proporcionar um ambiente colaborativo, onde as pessoas se ajudem com boa vontade e sem aqueles preconceitos competitivos do tipo "como é que você é analista se não sabe fazer isso?" Todo profissional possui seus pontos fortes e seus pontos a melhorar. Em um ambiente colaborativo, todos somente têm a ganhar com a troca de conhecimentos. Principalmente a empresa, com o consequente aumento da produtividade.

7) Não gaste seu tempo com bobagens: a internet é uma poderosa ferramenta para o desenvolvedor de software que sabe explorá-la. Portanto, aproveite-a durante o expediente, navegue e faça as suas pesquisas, mas com o objetivo de solucionar as questões inerentes ao seu trabalho. É claro que, quando bater aquele stress, não há problemas em ler os e-mails pessoais, fazer alguma ligação extra trabalho, ir tomar um café e conversar um pouco sobre assuntos aleatórios para descontrair. Não somos máquinas. Mas não exagere, mantenha o foco. Cada empresa por onde você passar oferecerá um leque de aprendizados técnicos e de negócio. Estude os códigos que seus colegas fizeram, aprenda "pulos do gato" que você desconhece. E, principalmente, procure realizar um bom trabalho e passar uma imagem verdadeira de dedicação, tanto nas atividades mais empolgantes quanto nas mais "burocráticas". Não procrastine. Não adianta ser um excelente técnico e deixar uma fama de preguiçoso e/ou sem foco por onde passa.

8) Teste exaustivamente o que desenvolveu: atualmente, o mercado de desenvolvimento de software oferece ferramentas bastante úteis de testes unitários. É recomendável utilizá-las, a fim de documentar os testes realizados e, também, como forma de mostrar a um novo colega desenvolvedor as formas possíveis de acesso e utilização dos métodos existentes. Por outro lado, esta forma de automatização de testes não exime o desenvolvedor de "utilizar o boné do usuário" e navegar pelo software que desenvolveu. Desta forma, antes de afirmar que o seu trabalho está concluído, navegue bastante pelo software, faça buscas, cadastros, atualizações e exclusões. Procure testar o máximo de cenários que conseguir imaginar. Se possível, passe o software para outros colegas testarem também. Eles podem imaginar outros cenários que possam gerar falhas. Não seja visto como "aquele cara que leva dois minutos para realizar o trabalho e dois meses para deixá-lo funcionando corretamente".

9) Documentação escrita em português estruturado não é bobagem: se você acha que documentar um software somente com um diagrama de classes é o suficiente e que o resto é bobagem, está cometendo um ledo engano. Por mais usabilidade que um software possa oferecer, existem regras de negócios e formas de navegação que devem ser documentadas. Portanto, um manual de usuário demonstrando as formas de navegação do software e uma documentação técnica explicando os casos de uso em português estruturado, versão do framework utilizada e, também, com diagramas que sejam úteis, entre outros, serão sempre bem vindos para o time, principalmente para seus membros que ainda não conhecem o software e que, em uma empresa humana e saudável, não possuem a menor obrigação de adivinhar o que aconteceu no passado do desenvolvimento daquele software específico. Ah, é claro, uma vez escrita a documentação, a mesma deve ser atualizada conforme o software seja modificado, para não confundir a cabeça do time e não transformar um trabalho minucioso em "papelada para fogueira de São João". Se você não possui uma redação razoável em português e, por isso, reluta em documentar o que você faz, a sugestão é que você se aprimore no nosso idioma, tanto na leitura quanto na escrita. Mas não diga que algo é ruim só porque você não sabe ou não gosta de fazer. Observação: não é necessário ser um professor Pasquale para escrever uma documentação. Basta ser claro nas informações que deseja passar.

10) Não fuja do material de estudo em inglês: grande parte do material de qualidade na internet e das boas literaturas de desenvolvimento de software está no idioma inglês. Busque proficiência neste idioma, que é de extrema importância não somente para a parte técnica, mas também lhe auxiliará a abranger boas oportunidades profissionais.

11) Seja objetivo: quando um colega quiser tirar uma dúvida sobre o código que precisará mexer, atenha-se exatamente ao que ele está perguntando. Não dê voltas tentando demonstrar toda a sua erudição técnica, pois, desta forma, ele, além de não entender a sua explicação, poderá achá-lo arrogante (se o colega for levar para o lado da maldade) ou maluco (se o colega for levar para o lado da piedade).
A objetividade também deve sempre ser levada para o desenvolvimento do software, desde seu nome, até as camadas que farão parte dele. Alguns exemplos:
a) Se o objetivo do software for "estoque de produtos", é muito mais prático e claro nomeá-lo "Estoque de Produtos" do que "Thunder Cats".
b) Nomes de variáveis, métodos e camadas são muito importantes para que o time entenda o software. Então, não abuse da criatividade e nomeie, por exemplo, a variável de quantidade de produtos como qtdProdutos em vez de observeAndCount e o método de obtenção de produtos como ObterProdutos() em vez de GetThings(). Se o objetivo de uma camada da arquitetura for acesso a dados, basta nomeá-la com o seu objetivo primário, em vez de chamá-la de "Data Cave".
c) Há uma corrente que diz que código bem feito não precisa de comentários. No mundo dos sonhos, é assim que a coisa funciona. Porém, no mundo real, o código pode estar muito bem feito, mas sempre há aquele trecho meio obscuro, não tem jeito. Há situações em que é de muito bom gosto inserir um comentário que explique o que determinado trecho de código faz. Afinal, "comentários existem para comentar".
d) Não reinvente a roda: não faça um código de difícil entendimento só porque é bonito e utiliza recursos eruditos. O que é bonito aos seus olhos pode ser aterrorizante aos olhos dos outros. O que pode ser simples, deve sempre continuar simples.

segunda-feira, 27 de junho de 2011

Design Patterns

Artigo traduzido de http://sourcemaking.com/design_patterns

Na engenharia de software, um design pattern (padrão de projeto) é uma solução geral para um problema comum de ocorrer no design de software. Um design pattern não é um projeto concluído que possa ser transformado diretamente em código. É uma descrição ou modelo (de como resolver um problema) que pode ser usado em muitas situações diferentes.

Design patterns podem acelerar o processo de desenvolvimento, oferecendo paradigmas de desenvolvimento testados e provados. Um projeto de software eficaz deve prever questões para que não haja problemas na implementação. O reúso dos design patterns ajuda a prevenir problemas maiores, além de melhorar a legibilidade do código para os programadores e arquitetos familiares com os patterns.

As pessoas freqüentemente sabem aplicar somente algumas técnicas de projeto de software para sanar certos problemas. Estas técnicas são difíceis de aplicar a uma gama mais ampla de problemas. Design patterns oferecem soluções gerais, documentadas em um formato que não requer especificidades ligadas a um problema particular.

Além disso, os patterns permitem aos desenvolvedores se comunicarem utilizando nomes conhecidos nas interações de software. Podem-se melhorar os design pattterns ao longo do tempo, os tornando mais robustos que designs ad-hoc (significado: para resolver determinado problema ou realizar uma tarefa específica).

Creational Design Patterns: dizem respeito à instanciação de classes. Podem ser divididos em patterns de criação de classes e patterns de criação de objetos. Enquanto os patterns de criação de classes utilizam herança no processo de instanciação, os patterns de criação de objetos utilizam delegação. Nomes dos design patterns criacionais (que serão explicados em outros posts): Abstract Factory, Builder, Factory Method, Object Pool, Prototype, Singleton.

Structural design patterns: dizem respeito à composição da classe e do objeto. Os patterns estruturais de classes utilizam herança para compor interfaces. Os patterns estruturais de objetos definem formas de compor objetos a fim de obter uma nova funcionalidade. Nomes dos design patterns estruturais (que serão explicados em outros posts): Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Private Class Data, Proxy.

Behavioral design patterns: dizem respeito à comunicação dos objetos da classe. Nomes dos design patterns estruturais (que serão explicados em outros posts): Chain of responsibility, Command, Interpreter, Iterator, Mediator, Memento, Null Object, Observer, State, Strategy, Template method, Visitor.


Críticas:

O conceito de design patterns tem sido criticado por algumas correntes da ciência da computação.

a) Aponta o problema errado: a necessidade de padrões resultam do uso de linguagens de computador ou técnicas com habilidade de abstração ineficiente. Num projeto ideal, um conceito não deve ser copiado, mas meramente referenciado. Mas se algo é referenciado em vez de copiado, não há padrão para rotular e catalogar. Paul Graham assim escreve em Revenge of the Nerds.

Peter Norvig também tem um argumento similar. Ele demonstra que 16 em 23 patterns no livro Design Patterns (que é primariamente focado em C++) são simplificados ou eliminados (através do suporte direto da linguagem) em Lisp ou Dylan.

Carece de fundamentos formais: o estudo dos design patterns têm sido excessivamente ad hoc, e alguns argumentam que o conceito necessita ter um posicionamento mais formal. Na OOPSLA 1999, a Gang of Four foi submetida a um "julgamento", em que foi "condenada" por ⅔ dos "jurados" que participaram do "julgamento" por "crimes contra a ciência da computação".

Leva a soluções ineficientes: a idéia do design pattern é uma tentativa de padronizar o que já é considerado melhor prática. Em princípio isto pode parecer benéfico, mas na prática sempre resulta na duplicação desnecessária de código. É quase sempre mais eficaz utilizar uma implementação bem feita do que um "mero design pattern".

Não difere significantemente de outras abstrações: alguns autores alegam que os design patterns não diferem significantemente de outras formas de abstração, e que o uso da nova terminologia (emprestada da comunidade de arquitetura) para descrever o fenômeno existente no campo da programação é desnecessário. O paradigma Model-View-Controller é aclamado como um exemplo de um "pattern" que precede em muitos anos o conceito de "design patterns". Também há o argumento de que a contribuição primária da comunidade Design Patterns (e o livro da Gang of Four) foi o uso do padrão de Alexander como uma forma de documentação; uma prática que freqüentemente é ignorada na literatura.

Creational patterns

Artigo traduzido de http://sourcemaking.com/creational_patterns.

Na engenharia de software, design patterns criacionais lidam com mecanismos de criação de objetos. A intenção é criar objetos da forma apropriada à situação, uma vez que forma básica da criação de objetos pode resultar em problemas de design ou adicionar complexidade ao design. Os design patterns criacionais resolvem este problema controlando de alguma maneira a criação do objeto:

  • Abstract Factory: Cria uma instância de diversas famílias de classes.

  • Builder: Separa a construção do objeto de sua representação.

  • Factory Method: Cria uma instância de diversas classes derivadas.

  • Object Pool: Evita aquisição e liberação custosa de recursos através da reciclagem de objetos que não estão mais em uso.

  • Prototype: Uma instância completamente inicializada que é copiada ou clonada.

  • Singleton: Uma classe onde somente uma instância pode existir.

Regras importantes:

Às vezes, padrões de criação são concorrentes: há casos em que Prototype ou Abstract Factory podem ser usados de forma benéfica. Em outros momentos, são complementares: Abstract Factory pode armazenar um conjunto de Prototypes onde os objetos são clonados e retornados; o Builder pode usar um dos outros padrões para implementar os componentes que são construídos. Abstract Factory, Builder e Prototype podem usar Singleton na sua implementação.
  1. Abstract Factory, Builder e Prototype definem um objeto de fábrica que é responsável por conhecer e criar a classe de objetos de produto e o torna um parâmetro do sistema. Abstract Factory tem o objeto de fábrica produzindo objetos de diversas classes. Builder tem o objeto de fábrica que constrói um produto complexo incrementalmente, usando um protocolo complexo correspondente. Prototype tem o objeto de fábrica (aka prototype) que constrói um produto através da cópia de um objeto de protótipo.

  2. As classes da Abstract Factory são implementadas por Factory Methods, mas também podem ser implementadas usando Prototype.

  3. Abstract Factory pode ser utilizada como uma alternativa à Facade para esconder classes específicas da plataforma.

  4. Builder foca na construção passo a passo de um objeto complexo. Abstract Factory enfatiza a família de objetos de produto (simples ou complexos). Enquanto no Builder o produto é retornado como uma etapa final, no Abstract Factory o produto é retornado imediatamente.

  5. Builder está para criação assim como Strategy está para algoritmo.

  6. Builder sempre constroem um Composite.

  7. Factory Methods são normalmente chamados dentro de Template methods.

  8. Factory Method: criação através de herança. Prototype: criação através de delegação.

  9. O design freqüentemente começa usando Factory Method (menos complicado, mais customizável, subclasses proliferam) e evolui para Abstract Factory, Prototype ou Builder (mais flexíveis, porém mais complexos) conforme o designer descobre onde é necessária uma maior flexibilidade.

  10. Prototype não requer subclasse, mas requer uma operação Initialize. Factory Method requer subclasse, mas não requer Initialize.

  11. Designs que utilizam padrões Composite e Decorator podem também obter benefícios do Prototype.

sábado, 9 de outubro de 2010

O que é Anti-Pattern?

Este texto é uma tradução da Wikipedia

Em Engenharia de Software, um anti-pattern é um pattern que pode ser utilizado, mas é ineficiente e contraprodutivo na prática.

O termo foi cunhado em 1995 por Andrew Koening, inspirado no livro Design Patterns da Gang of Four, que desenvolveu o conceito de design patterns na área de software.
O termo foi popularizado três anos depois pelo livro AntiPatterns, que estendeu o uso do termo além das fronteiras do design de software para a interação social geral. De acordo com os autores, devem haver pelo menos dois elementos-chaves presentes para distinguir formalmente um anti-pattern de um simples mau hábito, má prática, ou má idéia:

- Alguns patterns repetidos de ação, processo ou estrutura que inicialmente parecem ser benéficos, mas acabam produzindo mais conseqüências ruins que que resultados benéficos;
- Uma solução refatorada é claramente documentada, comprovada na prática e repetível.

Muitas idéias anti-patterns são mais que erros, retórica, problemas sem solução ou práticas ruins que devem ser evitadas. O uso informal do termo, às vezes denominado "armadilhas" (pitfalls) ou "patterns negros" (dark patterns), refere-se a soluções ruins para os problemas. Desta forma, muitos anti-patterns colocados sob debate não seriam formalmente considerados anti-patterns.

Através da descrição formal de erros repetidos, podem-se reconhecer as forças que levam a esta repetição e aprender refatorar este pattern.

Anti-patterns Organizacionais:
- Paralisia na análise: colocar um esforço desproporcional na fase de análise de um projeto.
- Vaca leiteira: Um produto legado rentável que muitas vezes leva à complacência sobre os novos produtos.
- Design por commitê: resultado de muitos contribuintes para o design, mas sem uma visão unificada.
- Escalação de compromisso: falha em revogar uma decisão quando a mesma se revela errada.
- Gestão por perkele (sem tradução): estilo autoritário de gerenciamento, sem tolerância à dissidência.
- Gestão matricial: estrutura organizacional sem foco, que resulta em subordinação dividida e falta de direção.
- Risco moral: isentar um tomador de decisão das conseqüências de sua decisão.
- Gestão Cogumelo: manter funcionários sem informação ou mal informados.
- Stovepipe ou Silos (sem tradução): uma estrutura que suporta principalmente o fluxo vertical (de cima para baixo) de informação, mas inibe a comunicação organizacional.
- Vendedor bloqueado: tornar um sistema excessivamente dependente de um componente externo.

Anti-patterns de Gerenciamento de Projetos:
- Marcha para a morte: todos sabem que o projeto caminha para um desastre, exceto o presidente da empresa. Porém, a verdade permanece ocultada e a sobrevida do projeto é mantida artificialmente até que o "Dia D" chega. Definição alternativa de marcha para a morte: funcionários sofrem pressões para trabalharem dias, noites e fins de semana em um projeto com prazo irracional.
- Groupthink (pensamento em grupo): durante o groupthink, membros do grupo evitam mencionar pontos de vista que estejam fora da zona de conforto do pensamento consensual.
- Fumaça e espelhos: demonstrar como funções não implementadas irão surgir.
Software bloat (inchaço do software): permitir versões sucessivas de um sistema a fim de demandar cada vez mais recursos.

Anti-patterns de análise:
- Apatia do espectador: quando um requisito ou uma decisão de design está incorreta, mas as pessoas que notaram o problema não fazem nada a respeito, porque isto afeta um número maior de pessoas.

Anti-patterns do design de Software:
- Inversão da Abstração: não expor a funcionalidade implementada requisitada pelos usuários, para que eles a re-implementem usando funções de mais alto nível.
- Ponto de vista ambíguo: apresentar um modelo (normalmente análise e design orientados a objeto) sem especificar seu ponto de vista.
- Grande bola de lama: um sistema sem nenhuma estrutura reconhecida.
- Database-as-IPC: usar um banco de dados como message queue para comunicação de interprocessamento de rotina onde um mecanismo muito mais simples seria apropriado.
- Chapeamento a ouro: continuar a trabalhar em uma tarefa ou projeto bem além do ponto em que o esforço extra agrega valor.
- Efeito na plataforma interna: um sistema é tão customizável que se torna uma réplica pobre da plataforma de desenvolvimento de software.
- Input kludge (sem tradução): falha em especificar e implementar o tratamento de uma entrada possivelmente inválida.
- Interface bloat (inchaço da interface): construir uma interface tão poderosa que se torna extremamente difícil de implementá-la.
- Magic pushbutton (botão mágico): codificar a lógica de implementação diretamente no código da interface, sem usar abstração.
- Race hazard (perigo de corrida): falha na verificação da conseqüência de diferentes ordens de eventos.
- Stovepipe system: componentes mal-relacionados e difíceis de manter.

Anti-patterns do design orientado a objeto:
- Anemic Domain Model: uso do modelo de domínio sem qualquer lógica de negócio. Os objetos do modelo de domínio não podem garantir sua correção, porque sua lógica de validação e mutação está armazenada externamente (muito provavelmente em múltiplos locais).
- BaseBean (sem tradução): herdar a funcionalidade de uma classe utilitária em vez de delegar a ela.
- Call super (sem tradução): requisitar às subclasses a chamarem um método overriden de uma superclasse.
- Problema círculo-elipse: subtipar tipos-variável com base dos subtipos de valor.
- Dependência circular: introduzir dependências mútuas diretas ou indiretas entre objetos ou módulos do software.
- Constant interface (sem tradução): usar interfaces para definir constantes.
- God object (Objeto Deus): concentrar muitas funções em uma única parte do design (classe).
- Object cesspool (fossa de objetos): reutilizar objetos cujo estado não está em conformidade com o contrato (possivelmente implícito) para reúso.
- Orgia de objetos: falha para encapsular propriamente os objetos, permitindo acesso irrestrito ao seu interior.
- Poltergeists: objetos cujo único propósito é passar informação para outro objeto.
- Acoplamento seqüencial: uma classe que exige que seus métodos sejam chamados em uma ordem particular.
- Problema Iô-Iô: uma estrutura (por exemplo, de herança) que é difícil de entender devido à sua fragmentação excessiva.

Anti-patterns de programação:
- Complexidade acidental: introduzir uma complexidade desnecessária a uma solução.
- Ação a distância: interação inesperada entre partes amplamente separadas de um sistema.
- Fé cega: falta de verificação da correção realizada em um bug, ou do resultado de uma subrotina.
- Âncora de barco: manter uma parte do sistema que não possui mais qualquer uso.
- Busy spin (sem tradução): consumir CPU enquanto aguarda algo ocorrer, normalmente pela verificação repetida em vez de mensagem.
- Caching failure (sem tradução): esquecer de resetar uma flag de erro quando o mesmo foi corrigido.
- Cargo cult programming (sem tradução): usar patterns e métodos sem entender por quê.
- Codificar por exceção: adicionar novo código para tratar cada caso especial reconhecido.
- Esconder o erro: fazer um catch da mensagem de erro antes que a mesma seja mostrada ao usuário e não mostrar nada ou uma mensagem sem significado algum.
- Hard code: incorporar pressupostos do ambiente de um sistema em sua implementação.
- Fluxo de lava: manter código indesejável (redundante ou de baixa qualidade) porque removê-lo é muito caro ou trará conseqüências imprevisíveis.
- Seqüência Loop-switch: codificar um conjunto de etapas seqüenciais usando switch dentro de um loop.
- Números mágicos: incluir números inexplicáveis em algorítmos.
- Strings mágicos: incluir strings literais no código, para comparações, como tipos de eventos etc.
- Soft code (código leve): não armazenar a lógica de negócio no código fonte, mas em arquivos de configuração.
- Código macarrônico: programas cuja estrutura é difícil de compreender, especialmente por causa da mistura de estruturas de código.

Anti-patterns metodológicos:
- Copiar e colar um programa: copiar e modificar um código existente em vez de criar soluções genéricas.
- Golden hammer (martelo de ouro): presumir que uma solução favorita é universalmente aplicável.
- Fator de improbalidade: presumir que seja improvável que um erro conhecido possa ocorrer.
- Otimização prematura: codificar pensando somente na eficiência, sacrificando o bom design, manutenibilidade e, muitas vezes, a própria eficiência.
- Programar por acidente: tentar chegar a uma solução através de modificações sucessivas no código para ver se funciona.
- Reinventar a roda: não adotar uma solução existente e adequada.
- Reiventar a roda quadrada: não adotar uma solução existente. Em vez disso, adotar uma solução customizada com um desempenho muito pior que a existente.
- Silver bullet (bala de prata): presumir que uma solução técnica favorita pode resolver um processo ou problema maior.
- Tester Driven Development (Desenvolvimento Orientado ao Testador): projetos de software onde novos requisitos são especificados em relatórios de erros.

Anti-patterns de gerenciamento de configuração:
- Dependency hell: problemas com as versões dos produtos exigidos.
- DLL hell: gerenciamento inadequado das dynamic-link libraries (DLLs).
- Conflito de extensão: problemas com diferentes extensões para as versões do pre-Mac OS da MC OS, na tentativa de corrigir as mesmas partes do sistema operacional.
- JAR hell: superutilização de múltiplos arquivos JAR, normalmente causando problemas de versionamento e localização, devido ao não entendimento do Java class loading model.

Tester Driven Development: Não Confundir com Test Driven Development

Este texto é uma tradução do Wikipedia

Tester driven development é um anti-pattern no desenvolvimento de software. Não pode ser confundido com Test Driven Development (TDD). Este anti-pattern diz respeito a qualquer projeto de desenvolvimento de software onde a fase de teste é tão longa que os requisitos podem mudar radicalmente durante o teste do software. Requisitos novos ou alterados acabam surgindo como relatórios de bugs. Um software de rastreamento de bugs normalmente não oferece suporte para lidar com requisitos. Como resultado, ninguém sabe de fato quais são os requisitos do sistema.

Projetos que são desenvolvidos utilizando este anti-pattern sempre sofrem grandes atrasos. Outro problema freqüente é a queda na qualidade do código.

As causas mais comuns para projetos que acabam sendo executados dessa maneira, são:

A fase de teste começa muito cedo;
Requisitos incompletos;
Testadores inexperientes;
Desenvolvedores inexperientes;
Má gerência de projetos.

As coisas pioram quando os testadores percebem que não sabem quais são os requisitos e, portanto, não sabem como testar uma mudança particular no código. Desta forma, o ônus recai sobre os desenvolvedores de alterações para escreverem seus próprios casos de teste.

sexta-feira, 8 de outubro de 2010

Anemic Domain Model

Este post é uma tradução do Wikipedia.

O Anemic Domain Model é um termo usado para descrever o uso de um software domain model, onde a lógica de negócio é implementada fora dos objetos de domínio.

Resumo
Este pattern foi descrito primeiramente por Martin Fowler, que considera a prática um anti-pattern. Com este pattern, a lógica é tipicamente implementada em classes separadas que transformam o estado dos objetos de domínio. Fowler denomina estas classes externas como transaction scripts (scripts de transação). Este pattern é uma abordagem comum em Enterprise Java Applications, possivelmente encorajado por tecnologias como as versões iniciais do EJB Entity Beans, assim como aplicações .Net que seguem a arquitetura de aplicação de serviços em três camadas, onde tais objetos se enquadram na categoria de "Entidades de Negócios" (embora Entidades de Negócio possam conter comportamento).

Benefícios:
Separação clara entre lógica e dados (programação procedural).


Deficiências:
- Lógica não pode ser implementada de forma verdadeiramente orientada a objeto a não ser que wrappers sejam usados para esconder a estrutura anêmica de dados.
- Violação do encapsulamento e dos princípios de ocultação de informação.
- Necessita de uma camada de negócios separada para conter a lógica localizada em um modelo de domínio. Isto também significa que objetos do modelo de domínio não podem garantir sua correção, porque sua lógica de validação e mutação é colocada em algum local externo (muito provavelmente em múltiplos locais).
- Necessita de um acesso global aos internos das entidades de negócio compartilhadas, aumentando acoplamento e fragilidade.
- Facilita a duplicação de código entre scripts transacionais e casos de uso similares, além de reduzir o reúso de código.
- Necessita de uma camada de serviço quando for compartilhar a lógica de domínio entre diferentes consumidores de um modelo de objeto.
- Torna um modelo menos expressivo e mais difícil de entender.

God objects: em geral, uma má prática

Este post é composto de trechos extraídos da Wikipedia.

Em programação orientada a objeto, um God Object (Objeto Deus) é um objeto que sabe muito e faz muito. God Object é um exemplo de anti-pattern.

A idéia básica por trás da programação estruturada é que um problema grande seja quebrado em muitos problemas menores (estratégia dividir e conquistar) e soluções são criadas para cada um destes problemas. Assim que os problemas pequenos são resolvidos, o problema como um todo também será. Portanto, há somente um objeto de quem um objeto precisa saber tudo: ele mesmo. Além disso, há somente um objeto de quem um objeto deve resolver um conjunto de problemas: ele mesmo.

O código baseado em God Object não segue esta abordagem. Em vez disso, a maioria da funcionalidade global é codificada em um único objeto "sabe-tudo", que mantém a maioria das informações do programa inteiro e fornece a maioria dos métodos para manipular os dados. Devido ao fato que este objeto mantém muitos dados e requisita muitos métodos, seu papel no programa se torna como se fosse um "Deus". Em vez de objetos se comunicando diretamente, os outros objetos dentro do programa confiam a um God Object a maioria de suas informações e interações. Uma vez que o God Object é referenciado por muitos outros códigos, a manutenção se torna mais difícil do que em um design dividido de forma mais equilibrada.

God Object é um análogo orientado a objeto de não usar subrotinas em linguagens procedurais, ou em usar muitas variáveis globais para armazenar informação de estado.

Enquanto a criação de um God Object é normalmente considerada uma má prática de programação, esta técnica é ocasionalmente utilizada em certos ambientes de programação (como microcontrollers), onde há um ligeiro aumento de desempenho e a centralização do controle é mais importante que a manutenibilidade e o código elegante.