Mês: abril 2013

Tasks MSBuild para métricas simples

Para resolver um problema pontual, codifiquei a toque de caixa duas tasks MSBuild para tirar algumas métricas de código. Provavelmente tem outra coisa por aí, melhor escrita, mas com a melhor das intenções, estou publicando para que possam usar, criticar ou colaborar.

O objetivo das tasks são:

  • CountFilesByExtension: Varre uma lista de arquivos e retorna a quantidade de arquivos por extensão
  • CountProjectsByProjectType: Varre uma lista de arquivos, abre os arquivos e identifica o tipo de projeto (Library, Windows, etc). Retorna a quantidade de projetos deste dado tipo

Código no git hub: https://github.com/ericlemes/EricLemes.MSBuildTasks

Anúncios

Inversão de Dependências

Introdução

unclebob

Neste post, gostaria de falar um pouco de umas impressões que tive sobre o livro “Designing Object-Oriented C++ Applications Using the Booch Method”, do Robert C. Martin (aka Uncle Bob).

Comecei a ler este livro com o objetivo de “beber na fonte”, voltar lá para os primórdios do início do desenvolvimento orientado a objeto para tentar entender como chegamos na complexidade que atualmente temos e também tentar entender se essa complexidade que estamos inserindo em nome da “orientação a objetos” tem fundamento mesmo e de onde vem esse fundamento.

Não sei se poderia ter uma fonte melhor para atingir meus objetivos. Este livro é (pasmem!) de 1995 e soa bastante atual. Vamos lembrar onde estávamos em 1995. Não sei qual era a realidade de vocês, mas na minha não se falava em UML, a escola técnica que freqüentei explicava o que é DFD (diagrama de fluxo de dados) e na minha realidade imperava o desenvolvimento em dBase, Clipper, e começávamos a sonhar em desenvolver aplicações “windows” em VB (não sei nem se na versão 3 ainda) e no meu caso Delphi 1.0, 16 bits. Windows 3.1 era talvez o sistema operacional mais popular.

É nesse contexto que o Uncle Bob falava sobre alguns tópicos que pretendo resumir aqui.

Gerenciando complexidade

É engraçado ler um livro de 1995 falando de uma “crise de software” em relação à como gerir a complexidade no desenvolvimento de software. Nada poderia ser mais atual. Ele também cita o aumento da demanda aos usuários (o aumento da expectativa) comparado com a velocidade que as técnicas de desenvolvimento de software evoluem.

O Uncle Bob cita neste livro que o grande motivador do design orientado a objetos era buscar uma forma de gerenciar melhor a complexidade do software.

Por que OOD é diferente?

Este ponto me gerou uma reflexão bastante profunda em como fazemos software. Se eu pensar no começo da minha carreira, basicamente o pensamento era baseado na análise estruturada. Pensávamos nos dados e em pequenos processos que extraiam os dados para o usuário, o usuário trabalhava esses dados e devolvia para o repositório. Tudo sempre muito orientado a dados, que talvez seja uma herança do COBOL/Mainframe que carregamos até hoje.

A estrutura de uma aplicação era muito simples. Um menu, em cada menu uma tela e em cada tela uma funcionalidade. Ninguém parava de fato para pensar em como organizar esta lógica ou como gerenciar melhor as dependências. Cada funcionalidade era gerada do zero, geralmente usando algumas bibliotecas de infra-estrutura com uma conotação fortemente técnica, quando muito existia uma “herança de form” (no caso do Delphi) para agilizar o desenvolvimento e abstrair a lógica de como o usuário operava a “tela”. Pouco falava-se de classes de domínio, camada de negócio ou qualquer coisa do tipo.

A explicação deste ponto dada pelo livro é muito interessante. Vou fazer uma tradução livre da resposta dada no livro para a pergunta “Por que design orientado a objetos é diferente?”:

UncleBob-OOD

“Imagine um design estruturado que consiste em módulos dispostos através de uma árvore. A raiz da árvore é o módulo principal. Os nós da árvore são módulos utilitários que atendem funções de baixo nível. A árvore representa uma hierarquia de chamadas. Cada módulo chama o módulo que está imediatamente abaixo e é chamado por módulos imediatamente acima.

Quais módulos capturam as políticas de importância primária da aplicação? Os que estão próximos do topo, é claro. Os módulos que estão abaixo simplesmente implementam os menores detalhes. Os módulos no topo estão preocupados com os maiores problemas e implementam as maiores soluções.

Assim, quanto mais alto se está na hierarquia, mais abstratos os conceitos são, mais pertinentes eles são para o domínio do problema da aplicação e menos pertinentes para o domínio da solução da aplicação. Quanto mais abaixo os módulos estão na hierarquia, mais detalhados os conceitos se tornam, menos relevantes eles são para o domínio do problema e mais relevantes eles são para o domínio da solução.

Dessa forma, os módulos que estão no topo chamam os módulos dele. Em outras palavras, os módulos que contem as abstrações relevantes dependem dos módulos que tem detalhes irrelevantes! Isso significa que alterações feitas nos detalhes afetam as abstrações e qualquer tentativa de reusar alguma dessas abstrações carregam junto os detalhes que delas dependem.

Em OOD tentamos inverter essas dependências. Nós criamos detalhes que dependem fortemente destas abstrações. Essa inversão de dependências é a principal diferença entre OOD (design orientado a objeto) e as técnicas tradicionais. ”

Vou tentar explicar o que entendi disso com um exemplo prático de uma implementação arcaica.

Se voltarmos para a época do dBase/Clipper (com uma idéia bem DOS), tínhamos o menu principal da aplicação que chamava diretamente um programa que cadastrava o pedido. Esse programa pegava o input do usuário e jogava para um programa que gravava o pedido. Esse por sua vez, chamava uma série de subprogramas para validar CEP, endereço, CPF, CGC (nesse tempo não tinha CNPJ!). Depois esse mesmo programa gravava os dados na tabela (tabelas DBF, é claro!). Todo o processamento de quando o pedido era feito, por exemplo, sensibilização de estoque era feito chamando também um subprograma de estoque que por sua vez puxava todos seus subprogramas de validação e também sua lógica de persistência no banco de dados.

Qualquer mudança realizada na lógica de estoque, poderia quebrar a lógica do pedido. Qualquer nova necessidade do pedido como por exemplo, gerar uma nota fiscal, demandava uma alteração nos programas de pedido. Ou seja, a lógica macro demandada por este pedido estava dependendo de detalhes de implementação de pedido.

As abordagens de separação em camadas resolvem grande parte do isolamento da lógica de apresentação, negócio e persistência, porém, algumas inerentes ao domínio de negócio são pensadas aqui. Por exemplo, quando falamos de sensibilização de estoque, se pensarmos num mesmo sistema, numa mesma estrutura de base de dados, é relativamente simples pensar em fazer uma chamada e atualizar o estoque, porém, se pensarmos num sistema externo, acessível através de um web service, por exemplo, essa lógica começa a ficar mais complexa e cada vez mais o pedido acoplado nisso.

A idéia para quebrar este conceito seria fazer uma abstração da interface de estoque, de forma que com diferentes implementações (transparentes para o pedido), o comportamento de como atualizar o estoque muda, sem a necessidade do pedido mudar. Aqui é que está essa idéia de “inverter as dependências”. O pedido não depende de como o estoque está implementado.

Poderia-se pensar mais além, e fazer uma abstração para “ações” executadas no domínio após a conclusão do pedido. Toda a lógica de gerar uma nota fiscal ou sensibilizar um estoque poderia ser implementada através dessas ações.

Fiz questão de usar um exemplo baseado no domínio de negócio para não “sujarmos” o conceito com a idéia de separação em camadas.

O interessante deste exemplo, é que mesmo que usemos técnicas modernas de domain driven design, patterns e separações de camadas, poderíamos continuar não enxergando essas abstrações e deixando o pedido acoplado na nota fiscal e no estoque.

Vale lembrar que utilizei esse domínio como um exemplo. Nem sempre colocar toda essa separação aqui pode ser uma boa idéia dependendo do sistema e da flexibilidade necessária.

Open-Closed Principle

Talvez este livro seja a publicação que definiu este princípio. A idéia aqui é que a partir do momento que encontramos essa abstração e conseguimos aplicar a inversão de dependências (no sentido que a lógica macro não depende dos detalhes), conseguimos criar classes que estão fechadas para alterações e abertas para extensões. Isso é considerado um dos principais princípios do design orientado a objetos.

No exemplo acima, a partir do momento que encontramos essa abstração para o pedido, ele passa a estar fechado para alterações (não preciso mexer mais nele para implementar novas ações) e aberto para extensões (implementando novas ações, ele pode ter seu comportamento extendido).

Contextos e Métricas

Outros pontos muito legais abordados neste livro é a idéia de contextos e métricas. No livro, Uncle Bob nos trás a idéia que a partir do momento que enxergamos as principais abstrações do software, conseguimos criar contextos. Ou seja, uma abstração e todas as implementações por trás dela consistem numa importante “unidade” no software.

A proposta é que essa unidade seja usada para divisão e composição de times de desenvolvimento, ou seja, como separar as atividades. É interessante perceber naquela época a preocupação dele com isso, ou seja, como os desenvolvedores conseguem ter alguma autonomia no design e continuar trabalhando de forma colaborativa. A idéia é que a partir do momento que exista uma convenção em como visualizar as abstrações, essa divisão é possível.

Para complementar ainda ele propõe um modelo de métricas para aferir se as dependências da aplicação estão bem equilibradas ou não, se existem classes que carregam muitas dependências. Em 1995!

Fazendo um paralelo com o exemplo aqui dado, um time poderia continuar a implementação do pedido e como acessar essa interface de ações. Outros times poderiam trabalhar no estoque e na nota fiscal com alguma liberdade no design e com a garantia que as coisas se encaixarão no futuro (garantidas pela interface).

Conclusão

Quando resolvi ler uma publicação antiga, escrita 18 anos antes deste post, meu principal objetivo foi tentar entender as motivações do design orientado a objeto e cruzar com diversas discussões que participo hoje. Vejo muita complexidade sendo adicionada desnecessariamente no software em nome da orientação a objetos.

Quando vi o conceito de “inversão de dependências” do Uncle Bob, na hora me identifiquei com a forma dele pensar. Hoje vejo softwares escritos em diversas camadas, utilizando várias tecnologias modernas, mas que não conseguem enxergar essas abstrações dentro do contexto de negócio e aplicar o conceito de inversão de dependências.

Alguns exemplos dessas situações, consigo citar de forma breve:

  • Aplicações em “n” camadas, utilizando MVC, ORM e outras coisas modernas, mas que possuem o problema de rateio n vezes no modelo, escrito e replicado em diversos lugares. Por que não criar uma abstração para isso?
  • Aplicações com “n” interfaces de integração que devido à diferença de formato, reescrevem toda a lógica de extração com regra de negócio replicada em várias interfaces. Geralmente as chamadas para interfaces de integração também estão diretamente acopladas no código.

Era isso!