Orientação a objetos: uma outra perspectiva sobre o acoplamento

Todo mundo que já viu orientação a objetos alguma vez na vida conhece a frase “classes devem ter alta coesão e baixo acoplamento“. Uma classe altamente acoplada pode ser sim um problema. Veja o diagrama abaixo, por exemplo, onde temos a classe GerenciadorDeNotaFiscal, que depende de outras 3 classes: um DAO, uma classe que envia e-mail e uma classe que se comunica com um serviço externo da empresa.

Acoplar-se a uma classe significa depender dela. E o problema de depender de outras classes é que, caso elas mudem, a classe principal pode sofrer mudanças também. No diagrama acima, se a classe Dao mudar sua interface ou mesmo o que ela faz, isso impactará na classe GerenciadorDeNotaFiscal. O mesmo aconteceria se as classes ServiçoExterno ou Email mudassem! Ou seja, quanto maior for a quantidade de classes dependentes, maior a chance de uma delas mudar e impactar em mudança na classe principal!

A ideia então é reduzir o acoplamento entre nossas classes o máximo possível. Mas sabemos que é impossível desenvolver sistemas com zero acoplamento. Mas será que todo e qualquer acoplamento é realmente problemático?

Por exemplo, a grande maioria dos sistemas em Java no mundo são acoplados com a interface List (afinal a necessidade de guardar conjuntos de elementos é presente na maioria das aplicações). E fazer uso dessa interface geralmente não é problema para os desenvolvedores. Esse é um acoplamento “que não ligamos muito”. A grande charada é entender a diferença entre o acoplamento com List e com o DAO, por exemplo.

A diferença é que a interface List não muda! Se ela não muda, isso significa que ela não forçará mudanças na classe que a usa! Ou seja, apesar de estarmos acoplados, o problema é menor, já que ela nunca provocará mudanças em nossos códigos!

Mas porque List não muda? Justamente por causa do acoplamento… o “outro lado do acoplamento”! Estamos sempre acostumados a ver o acoplamento como “a quantidade de classes que uma classe depende”; isso é conhecido como acoplamento eferente. Mas nunca vemos “a quantidade de classes que dependem de uma classe” (conhecido como acoplamento aferente).

A interface List, por exemplo, não depende de nada; mas muitas classes dependem dela. Veja a quantidade implementações da interface existentes: AbstractList, AbstractSequentialList, ArrayList, AttributeList, CopyOnWriteArrayList, LinkedList, RoleList, RoleUnresolvedList, Stack, Vector.

Isso sem contar a quantidade de classes que a referenciam hoje. Se algum engenheiro da Sun alterar essa interface, ele terá um trabalho imenso para propagar a mudança em muitos pontos. Portanto, provavelmente ele não fará isso! Isso torna essa interface estável!

Classes ou interfaces estáveis são aquelas que não tendem a mudar. Acoplar com classes ou interfaces com essa característica é interessante, pois sabemos que a chance dela mudar é baixa.

É por isso que interfaces são tão populares em códigos orientados a objeto. Interfaces tendem a ser estáveis, já que não possuem atributos ou implementação (coisas que tendem a mudar muito dentro de uma classe), e possuem muitas implementações embaixo dela (diminuindo a chance de algum programador mexer nela).

Entender bem as duas formas de acoplamento nos dá pontos de vista diferentes sobre a mesma classe. Se uma classe possui acoplamento eferente alto, isso significa que ela depende de muitas classes. E, para saber se esses acoplamentos são problemáticos, você pode medir o acoplamento aferente dessas dependências; se esse número for alto, essa classe tem a alta chance de ser estável, diminuindo o risco do acoplamento.

Evitar acoplamentos perigosos é tarefa importante. Você pode aprender mais sobre isso em nossos cursos online de Padrões de Projeto, no de Boas práticas em Orientação a Objetos ou em nosso livro de Test Driven Development.

37 Comentários

  1. Bruno A. da Costa 24/10/2012 at 11:01 #

    Não seria “Uma classe altamente acoplada pode ser sim um problema”
    no lugar de “Uma classe altamente pode ser sim um problema” ?

    Parabéns pelo post

  2. Roberto Souza 24/10/2012 at 11:06 #

    Valeu!! Gosto muito dos posta da Caelum de orientação a objetos.

  3. Reinaldo Vale 24/10/2012 at 11:10 #

    @mauricioaniche, boa explicação. Outra questão em relação ao bom acoplamento é a alta coesão. No seu exemplo a interface List é estável, também, pela forte coesão que suas assinaturas possuem em relação a uma lista, ou seja, no que tange os conceitos abstratos de uma lista.

  4. Mauricio Aniche 24/10/2012 at 11:43 #

    Oi Bruno,

    Obrigado! Já corrigi no texto!

    Oi Roberto,

    Que bom que gostou!

    Oi Reinaldo,

    Excelente comentário! A interface List é bem coesa! É bem difícil separar acoplamento de coesão, certo!? Essas duas coisas andam juntas o tempo todo! 🙂

  5. Alexandre Gama 24/10/2012 at 13:13 #

    Bem lembrado Aniche!

    Lembrando tb que esse baixo acoplamento facilita bastante os testes, principalmente nos nossos Mocks, que aliás é um bom momento pra identificar os nossos acoplamentos 🙂

    Abs!

  6. Mauricio Aniche 24/10/2012 at 13:16 #

    Oi Alexandre,

    É isso mesmo! Quanto menos acoplado é seu código, mais fácil é o teste!

    No meu livro de TDD, eu discuto bastante isso!

    Um abraço!

  7. Alexandre Gama 24/10/2012 at 13:23 #

    Dentre as partes bacanas que li no seu livro essa se encaixa na melhoria do design a partir dos testes, que é bem bacana pra galera ler.

    Novamente, parabéns pelo livro, ficou bem bacana! 🙂

    Abs!

  8. Mauricio Aniche 24/10/2012 at 13:25 #

    Obrigado, Alexandre! Fico feliz mesmo que tenha gostado!

  9. jonas 25/10/2012 at 10:37 #

    Aniche parabéns pelo post, vou comprar seu livro, onde encontrar em SP ?

  10. Mauricio Aniche 25/10/2012 at 12:33 #

    Oi Jonas,

    Você pode comprar pelo site da Casa do Código: http://www.casadocodigo.com.br/products/tdd

    Não esqueça de entrar na lista de discussão do livro também: tdd-no-mundo-real no Google Groups!

    Um abraço,
    Mauricio

  11. Emerson Antonio 25/10/2012 at 16:12 #

    Ótimo artigo, tendo em vista essa afirmação (a qual ja passei na pele, kkkk), qual a solução que poderiamos utilizar? Ja que no 1º exemplo, é necessário existirem as 4 classes interligadas e se comunicando?? Há uma padronização que solucione isso? Obrigado!

  12. Mauricio Aniche 25/10/2012 at 16:15 #

    Oi Emerson,

    Boa pergunta. Realmente não discuti isso, né?! Você pode usar padrões de projeto que ajudam a reduzir o acoplamento. Nesse caso, eu usaria um Observer, por exemplo.

    Acho que vou escrever um post sobre isso! O que acha?

    Um abraço!

  13. Germano 25/10/2012 at 23:29 #

    Muito bom seus textos Mauricio!
    Fiquei pensando em como ficaria usando Observer neste caso. Talvez se a classe GerenciadorDeNotaFiscal fosse um “Observable” e as demais classes fossem “Observer”. O manager poderia notificar um evento do tipo “NovaNotaFiscalLancada” e os observers fariam o que fosse preciso (persistir, enviar e-mail e notificar um serviço externo). Porém seria complicado se precisasse garantir a ordem de execução das dependências, não achas?
    Poderia fazer todas as classes em questão serem tanto “Observer” quanto “Observable” e, por exemplo, o Dao poderia notificar “NovaNotaFiscalGravada”, assim o serviço de email e o serviço externo poderiam ficar sabendo do evento e fariam o que fosse preciso. Seria uma forma de manter uma ordem…
    Seria muito legal se você escrevesse um post sobre isso!
    Abraço!

  14. Visitante 25/10/2012 at 23:32 #

    Excelenteee! Mas engenheiro da Sun? quando vc começou escrever esse artigo?

    Outra coisa, o que vc me diria da classe Object? ….

    paraaabéns!?

  15. Carlos Ruesta 26/10/2012 at 05:30 #

    Parabéns Mauricio, ficou show o post.

    No curso online resolve justamente esse problema com Observer, mas ficou bem mais claro o ponto de vista das classes estaveis do outro lado da parada e o porque tentar usar interfaces.

    Obrigado!!

  16. Alexandre Gama 26/10/2012 at 09:46 #

    Boa Emerson! Como o Aniche comentou poderia ser usado algum padrão mesmo.

    Aniche, neste caso uma simples injeção das interfaces dessas classes já não seria suficiente? Claro que só olhando o desenho fica mais complicado imaginar o que realmente o GerenciadorDeNotaFiscal vai fazer.

    Seria legal mesmo um post comentando possíveis soluções pra esse caso 🙂

    Abs!

  17. Mauricio Aniche 26/10/2012 at 12:49 #

    Oi Germano,

    É verdade! Se precisarmos garantir a ordem de execução, precisaremos trabalhar um pouco mais para isso! Mas nada que algumas linhas a mais de código não resolvam, certo?

    Uma solução mais genérica seria fazer com que um observador soubesse “se posicionar” perante aos outros. Por exemplo, o SAP sabe que deve ser executado depois do DAO, e coisa do tipo. Se tivermos esse grafo, basta ordenar topologicamente e teríamos a ordem correta de execução!

    É mais complicado, mas talvez uma abordagem válida!

    Oi Visitante,

    Puxa, a List é velha, certo? Devia ter tido “Joshua Bloch”, aí seria bem mais preciso! 🙂

    Oi Carlos,

    Puxa, não tinha pensado nisso! Que acha de colocarmos um link lá no online pro aluno ler esse post e dar a opinião dele?

    Oi Alexandre,

    Sim! Se vc receber uma List ou coisa do tipo, e invocar as ações, no fim é um Observer, certo? Vc só apelou pra um framework mágico para pegar os observadores!

    Vou trabalhar no post sim! Que acha de me ajudar? 🙂

    Abraço a todos!

  18. Germano 26/10/2012 at 17:36 #

    Gostei da idéia, um grafo deste seria útil até para fins de documentação, pois fica bem claro quais os processos que estão envolvidos e a ordem! Valeu pela resposta!

  19. Raphael Lacerda 30/10/2012 at 12:09 #

    Mais um excelente post da serie O.O…. o outro foi sobre encapsulamento!
    Quem dera ter esses posts do Aniche na epoca q estava aprendendo esses conceitos “abstratos”

  20. jonas 30/10/2012 at 20:57 #

    Aniche quando você afirma:
    // inicio
    Se uma classe possui acoplamento eferente alto, isso significa que ela depende de muitas classes. E, para saber se esses acoplamentos são problemáticos, você pode medir o acoplamento aferente dessas dependências; se esse número for alto, essa classe tem a alta chance de ser estável, diminuindo o risco do acoplamento.
    // fim
    Se ao medir o acoplamento aferente das classes da composição e perceber que o número é alto isso já automaticamente me garante um acoplamento estável ou próximo disso ? e o acoplamento eferente dessas classes não precisam se analisados ? Ou seja, o caminho inverso.
    Fiz esse comentário porque baseado no seu bom artigo achei a afirmação abaixo um pouco incompleta.

  21. Mauricio Aniche 31/10/2012 at 11:28 #

    Oi Jonas,

    Perfeito. O acoplamento eferente pode mostrar o outro lado, ou seja, o quão instável a classe é (afinal, se ela depende de várias classes, a chance dela mudar é alta). Para calcular instabilidade, você precisa olhar os dois acoplamentos!

    Um abraço!

  22. maico 07/11/2012 at 16:10 #

    Excelente!
    obs: Nem sempre o baixo acoplamento é necessário, e se percebermos onde ele não é necessário economizaremos tempo no desenvolvimento.

  23. Mauricio Aniche 07/11/2012 at 17:11 #

    Oi Maico,

    Perfeito! Uma frase que sempre digo (e acabei aprendendo no mestrado) é: SEMPRE e NUNCA são coisas que não devem ser ditas em engenharia de software! 🙂

    Um abraço,

  24. Bruno Bafilli 14/11/2012 at 00:04 #

    Ola Mauricio, eu quero comprar o curso de c# e gostaria de saber se todas as aulas sao feitas com video aula como a primeira que e de acesso gratuito por favor responda em meu email brunobafilli@gmail.com

    obrigado.

  25. vitor 11/12/2012 at 21:00 #

    Muito bom seu texto. É muito difícil encontrar textos com esta qualidade em português.

  26. Vanderson Assis 04/02/2013 at 11:47 #

    Grande Maurício!
    Cara achei interessantíssimo esse artigo. Parabéns!
    Velho só uma perguntinha. O que você acha melhor: Extender ou Implementar uma classe?

    Abraço e mais uma vez parabéns!

  27. Mauricio Aniche 04/02/2013 at 15:34 #

    Oi Vanderson,

    Depende do problema, na verdade. A vantagem da interface é que ela é “mais leve”, tende a ser estável, e logo menos problemática do ponto de vista do acoplamento.

    Mas também não tenho nada contra o bom uso de herança!

    É difícil discutir isso sem um problema concreto!

    Faz sentido?

    Um abraço,
    Mauricio

  28. Alexandre Leite 22/02/2013 at 02:00 #

    Parabéns pelo artigo. Muito didático. Em um artigo de 1994 (http://www.objectmentor.com/resources/articles/oodmetrc.pdf), Robert Martin propõe uma métrica de qualidade de software para avaliar se o acoplamento das classes é “saudável”. Em resumo, seria a medida do desvio de uma linha que liga os pontos de máximas abstração e estabilidade e o de suas mínimas. Esses pontos são medidos usando o número de acoplamentos aferentes e eferentes. No artigo do R. Martin também é proposta uma solução para um problema parecido com esse da nota fiscal. Vale a pena ler.

  29. Mauricio Aniche 22/02/2013 at 13:32 #

    Oi Alexandre,

    Excelente. Obrigado por compartilhar o link! 🙂

    Um abraço!

  30. Jader 12/09/2013 at 21:22 #

    Assisti uma palestra sua no Agile Brazil no ano passado sobre TDD e esse ano vi o short talk na QCon desse ano sobre métricas, muito bom.
    Comprei seu livro de TDD e como estou engatinhando nesse mundo, só de ler o livro não sei como consegui programar tanto sem testes. Mesmo com pouco tempo de leitura, já não consigo desenvolver sem pensar em testes.
    Sobre o short talk, gostei muito e gostaria de saber como posso aplicar métricas em meu projeto. Existe algum guia ?

  31. Mauricio Aniche 18/09/2013 at 13:38 #

    Oi Jader,

    Você pode ver o http://www.codesheriff.com. Ali vc encontra meu trabalho atual com métricas. Se olhar, não deixe de me dar feedback.

    Se quiser ler um pouco sobre métricas, a página do NDepend também é legal: http://www.ndepend.com/metrics.aspx

    Um abraço!

  32. Eduardo Falcão 07/05/2015 at 00:23 #

    Excelente exemplo, simples e objetivo. Também achei muito legal o paralelo com a interface List. 🙂

Deixe uma resposta