Arquitetura em prática

por Fabio Margarito Martins de Barros

Law of Demeter

A lei de Demeter é um conjunto de regras para construir sistemas visando baixo acoplamento, também conhecida como Princípio do menor Conhecimento e Fale somente com os amigos. Apesar do nome, Demeter não foi o autor, Demeter é o nome do projeto conduzido pela universidade Northeasterns University em 1987 liderado pelo Dr. Karl Lieberherr. Foi projeto Demeter que as leis foram criadas e por isto o nome.

Em linhas gerais, as regras são:

  • Cada unidade deveria somente utilizar um conjunto limitado de unidades de outras unidades: somente unidades fortemente relacionadas com a unidade corrente.
  • Cada unidade deveria falar somente com seus amigos e não com estrangeiros.

Em orientação a objetos, definido que uma unidade=método de um objeto, a lei é traduzida da seguinte forma:

Um método M de um objeto O somente poderia acessar métodos de outros objetos que sigam as diretrizes:

  • Seja parâmetro de M
  • Um objeto que M criou
  • Um método do próprio objeto O
  • Objeto diretamente relacionado com o objeto O
  • Uma variável global acessível pelo objeto O

Para não ficar muito abstrato, vamos aos exemplos:

Seja parâmetro de M: 

public class A{    
	public void FazAlgumaCoisa(B parametro){       
           parametro.FazOutraCoisa();}
}

Um objeto que M criou:

public class A{       
        public void FazAlgumaCoisa(){
           B objetoB = new B();
           B.FazOutraCoisa();}
        }

Um método do próprio objeto O

public class A{
    private void FazOutraCoisa(){
       //faz algo}        
    public void FazAlgumaCoisa(){
       FazOutraCoisa();}
}

Objeto diretamente relacionados com o objeto O

public class B{ 
        public void FazOutraCoisa(){
           //Faz algo}
}
public class A{      
            private B _objetoB;
            A(){_objetoB = new B();}
            public void FazAlgumaCoisa(){
               _objetoB.FazOutraCoisa();}
}

Um dos grandes problemas que temos na manutenção de sistemas OO é o alto acoplamento, ou seja, nossos objetos “falam” com muitos objetos, e quando isto acontece, se eu alterar um objeto no sistema, posso ter efeitos colaterais nos chamadores. A lei de Demeter como vimos, ajuda a diminuir este acoplamento. Algo do tipo

public class C{
        public void FazMaisOutraCoisa(){
          //Faz algo}
}

public class B{
        public C FazOutraCoisa(){
         return new C();}
}
public class A{
        private B _objetoB;
        A(){_objetoB = new B();}
        public void FazAlgumaCoisa(){
          _objetoB.FazOutraCoisa().FazMaisOutraCoisa();}
}

fere a lei de Demeter, pois não estou mais falando com um “amigo” e sim com o método de C que é o objeto de retorno de B, o “estrangeiro”. Agora nossa classe A tem acoplamento com B e C. Se alterar algo em C, tenho efeitos em A.  Sacaram ao que leva a lei de Demeter?

Um dos princípios de orientação a objetos que leva a concordância com a lei de Demeter, é o princípio da responsabilidade exclusiva do S.O.L.I.D. Porque digo isto? Simples, quando meu método começa a usar muitos recursos de outro objeto, em vez do próprio objeto ao qual pertence, provavelmente este método está no local errado, resumindo, não faz parte das responsabilidades do objeto que está. Outro princípio interessante de OO que ajuda a atingir a lei é o Tell don’t ask, mas este deixo para vocês lerem nas referências deste post.

Concordam com Lei, o que acham?

Referências: Law of Demeter, S.O.L.I.D, Tell don’t ask, Artigo do Giovanni Bassi sobre Tell don’t Ask

[]’s Fábio Margarito

Posted: fev 17 2010, 10:26 by fabiomargarito | Comentários (1) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Quer aprender TDD?

A galera do grupo Coding Dojo Floripa, postou aqui um excelente post com um apanhado de tutorias, vídeos entre outros recursos sobre TDD. Vale a pena dar uma olhada!

[]'s

Posted: fev 05 2010, 05:10 by fabiomargarito | Comentários (0) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: Boas práticas | Testes

Não confunda testes unitários com testes de sistema

Na prática, um percentual muito pequeno de desenvolvedores realiza algum tipo de teste nas aplicações, é triste, mas é a realidade. Sem testes(criados antes, ou depois da implementação de um código), torna-se impossível qualquer melhoria futura(Refactoring).

Ao observar equipes trabalhando, é perceptível que boa parte do tempo é gasto no Debug do Visual Studio(Ou ferramentas similares). A mecânica atual do trabalho de muitos desenvolvedores é a seguinte:

  • cria-se uma funcionalidade completa(Tela,Código, Banco)
  • roda-se a aplicação
  • o sistema falha(raramente roda da primeira vez sem nenhum bug)
  • inicia-se a epopéia no debugger para adivinhar onde ocorre o erro
  • corrige-se o bug
  • repete-se o ciclo até que todos os bugs foram corrigidos


Para mim é desperdício de tempo, e já justifica por si só, o tempo gasto na criação de um teste unitário. Eu Queria entender melhor, o motivo pelo qual eles trabalham da forma que relatei e questionei a  alguns desenvolvedores. Por quê vocês não fazem testes unitários? . As respostas foram bacanas:

  • "Testes unitários demoraram muito para serem criados, pois tenho que criar tela para testar o código, etc..."
  • "Você está enganado, já fazemos testes unitários, o responsável pelo teste pega a aplicação pronta e reproduz na UI os passos previstos em algum caso de testes"
  • "Testes unitários são complicados, tenho que colocar n referências no código fora a dificuldade de configurar o ambiente para os meus testes"

Na realidade, o problema não são os testes,  e sim a falta de conceitos, preparo dos profissionais e ausência de métricas que comprovem o desperdício de tempo e o real custo de um sistema(construção + manutenção).

Para não fugir do título do post e argumentar os motivos da não execução de testes unitários, vamos alinhar e entender a real diferença entre teste unitário e de sistema:

Testes unitários: São testes isolados que o desenvolvedor realiza em unidades de código isoladas, por exemplo, testar o método de uma classe. O teste unitário deve ser isolado e sem efeitos colaterais aos demais testes. Dependências externas(outros objetos, banco de dados,etc..), devem ser substituídas por mocks e stubs, pois a finalidade é testar aquele trecho de código em específico, e de presente você ainda cria aplicações menos acopladas. É ideal que os testes sejam automatizados, assim você os roda quantas vezes quiser a cada alteração de código. Se após a inclusão de uma melhoria, ou, adição de uma nova funcionalidade algum teste do conjunto falhar, é fácil identificar o bug, pois sei exatamente o que foi alterado, portanto, ganhamos produtividade. Existem n vantagem em utilizar testes, pontuei apenas algumas.

Testes de sistemas: São testes realizados na aplicação de modo integrado e pouco importa como a aplicação foi codificada. O teste é baseado nas funcionalidades solicitadas e requisitos não funcionais(usabilidade, desempenho, etc.). Existem vários tipos de testes de sistemas e estes devem ser executados conforme necessidade, tais como: Testes de usabilidade, de estresse, de carga de segurança entre outros. Para mais informações, consulte os links em saiba mais.

Saiba mais

Espero que tenha ficado clara a diferença e que as definições e material de apoio os ajudem a colocar os testes unitários no seu dia a dia, com certeza vocês não se arrependerão.


[]'s

Fabio Margarito

Posted: fev 04 2010, 02:58 by fabiomargarito | Comentários (1) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: Boas práticas | Testes

O perigo da concatenação de strings

Trabalho em uma grande instituição financeira e esta semana tivemos um problema sério de alto consumo de memória de um único processo, em um servidor de aplicação extremamente acessado. O consumo exagerado era de um processo de Cache, ele estava com quase 1GB de memória alocada.Bom, sendo um processo de Cache isto pode ser uma verdade em muitas situações, entretanto, a informação armazenada em Cache no momento, era de apenas 20 MB, portanto, 1GB alocado é algo fora dos padrões.

Descobrir memory leak não é uma tarefa muito fácil, realizar um Code Review pode ser uma solução, pois podemos percorrer o código e procurar por causas que podem determinar esta condição. Mas há um porem, se a aplicação for muito grande, isto pode demorar muito,e demorar a encontrar um problema de produção é muito grave para nosso setor de atuação.Quando há qualquer tipo de problema em produção, preferimos realizar análise de Dump por ser mais rápido.

O Dump do processo é gerado pelo pessoal de produção que nos encaminha para análise. Análise de Dump é uma técnica de debug onde analisamos a memória de um processo. No fim deste post, deixo algumas referencias para quem gosta de desafios.

Vamos lá, para realizar a análise vamos utilizar o programa WinDbg:

Pela opção File/Open Crache Dump, vamos carregar o Dump.  

 Agora vou carregar a DLL SOS.dll, esta DLL fornece comandos para manipular Dump de código gerenciado. Para cada versão do .Net Framework, há uma versão diferente. No caso, estou carregando a Dll do framework 2.0

Como é um memory leak, vou direto ao comando !dumpheap -stat. O objetivo deste comando é trazer estatísticas de consumo de memória por tipo. O resultado é expresso em bytes. Dependendo do tamanho do dump, o resultado poderá demorar um pouco. Por motivos de confidencialidade, estou ocultando algumas informações.

Achamos os vilões. Percebam que o tipo System.String ocupa 560 MB e temos em torno de 400 MB com objetos prontos para serem liberados. Somando, temos quase os 1GB de memória consumida. É um tanto estranho o tipo System.String ocupar tanto espaço não?Agora vamos recuperar todos os 281 mil objetos do tipo string. Para esta tarefa vamos usar o comando !dumpheap -mt 793308ec.

 Como são milhares de objetos, vamos pesquisar por amostragem. A idéia é buscar a raiz de cada objeto. Para realizar esta busca, vamos utilizar o comando !gcroot <endereço do objeto>, no caso !gcroot 3dc31000.

Temos um suspeito agora, o CacheCleanup. Varrendo outros objetos percebi que todos têm a mesma raiz, ou seja, provavelmente o problema está lá. Abaixo um trecho do código danoso: 

#region Access Log			
Handler.LogHandler.Tracking("Access Method: " + this.GetType().ToString() + "->" + ((object)MethodBase.GetCurrentMethod()).ToString() + " ;");
#endregion Access Log
Como podemos ver, o código está cheio de concatenações em um trecho crítico do projeto que trata traces, e este é chamado milhares de vezes. Concatenação de string com o operador + não é uma boa prática devido à maneira que ocorre a manipulação de memória. Mais informações você pode encontrar neste site. Para corrigir, apenas substituí por String.Builder e o problema foi resolvido.

Links

Blog da Tess Fernandes tem tudo sobre debug

Debugging Tools for Windows

Conclusão

O que pode ser algo inofensivo, em cenários onde há milhões de chamadas em curto espaço de tempo pode se tornar uma dor de cabeça sem precedentes e custar muito $$$$$.

[]’s

Fabio Margarito  

Posted: jul 22 2009, 23:51 by fabiomargarito | Comentários (7) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: Debug | Boas práticas