terça-feira, 6 de maio de 2008

Em busca do desacoplamento perdido.

No trabalho, tem sido recorrente conversas sobre boas práticas de design OO. Daí o surgiu a idéia de postar a essência destas discussões a fim de compartilhar as motivações, conclusões e mesmo abrir estas a críticas.
Uma polêmica recente foi sobre a implicações de criar um código em uma classe cliente com o seguinte estilo:

carro.getMotorOriginal().acelerar();

Em vez de:

carro.acelerar();

Onde:

class Carro{
...
public void acelerar(){
this.motorOriginal.acelerar();
}
...
}

No primeiro exemplo para um cliente acelerar um carro ele tem que obter o motor deste e explicitamente acelerar o motor.
No segundo exemplo para se fazer a mesma coisa, o cliente acelera o carro e o carro se encarrega de acelerar o motor.
A conclusão que se chegou é que na primeira situação o cliente está acoplado demais a estrutura interna do carro, a ponto de quando eu trocar o motor atual por um motor paraguaio, terei que alterar o cliente.

carro.getMotorParaguaio().aserejer();

O que é uma tremenda desvantagem em comparação com o segundo caso, pois neste o cliente se mantém o mesmo, o que mudaria seria a implementacao do método acelerar da classe carro.

class Carro{
...
public void acelerar(){
this.motorParaguaio.aserejer();
}
...
}

Confinando as mudanças a classe Carro.

Os chatos de plantão poderiam dizer que se eu fizer o Motor Original e o Motor Paraguaio implementar uma mesma interface Motor e se o cliente depender desta interface, eu poderia trocar de motor sem custos pro cliente. O que posso argumentar é que ainda que isso seja verdade o cliente continuaria dependendo da estrutura interna do Carro, continuaria sabendo que o carro tem um motor e continuaria acelerando este explicitamente, o que é uma clara violação do encapsulamento do carro(papo pra outro post). Sendo que a única coisa que ele precisa saber é que o carro é acelerável.

Formalizando a conclusão de que a segunda abordagem é superior a primeira, existe um princípio de design, a lei de Demeter, da qual posto aqui o link para os interessados em aprofundar design skills. Leitura recomendada. Até a próxima a todos e como diria a minha mãe e a lei de Demeter “Only talk to your immediate friends.”

http://en.wikipedia.org/wiki/Law_of_Demeter


PS: O exemplo com o motor paraguaio surgiu somente para fazer a referência pop ao hit Asereje do grupo musical espanhol Las Ketchups, que foi interpretatado aqui no Brasil pelo Grupo musical Rouge e rebatizado de Ragatanga.

Um comentário:

Pedro Cavaléro disse...

Concordo plenamente. O acoplamento é sempre feito com a classe mais próxima. Esse exemplo é como o meu relacionamento com os carros, teoricamente não precisaria saber q eles tem um motor. Na verdade eu descobri isso outro dia quando vi o meu cunhado acelerando o meu carro direto no motor heheh.
Ainda tenho pensado naquele negócio do Wrapper... é difícil chegar a uma conclusão. Uma coisa eu penso, que colocar informações de GUI no objeto de domínio não é uma boa prática (idem de BD, por semelhança) agora, onde vão estar essas informações ainda não consegui ver... Os frameworks web como o Struts 2 tem como conceito uma classe que transforma o dados como ele é estruturado para aparecer na tela em como ele persiste no objeto de nogócio. Talvez o uso do Beans Validator estivesse nesse ponto, pegando informações do metadado. Mas ainda estou pensando...