Coesão é uma métrica que mostra quão intimamente relacionados estão os elementos dentro de um módulo ou classe. De acordo com a Wikipedia, alta coesão é uma característica de um módulo bem projetado, onde todos os métodos e campos trabalham em uma única tarefa. Coesão afeta diretamente a manutenibilidade do código e se contrapõe ao acoplamento — a interconexão entre módulos.
Pontos principais
Coesão é uma métrica que avalia quão logicamente conectados estão os métodos, campos e propriedades dentro de uma classe ou módulo. Um módulo altamente coeso realiza uma tarefa e contém apenas os elementos necessários para realizá-la. Um módulo com baixa coesão tenta fazer várias coisas ao mesmo tempo — seus métodos estão fracamente relacionados em significado.
No contexto da programação orientada a objetos, a coesão está intimamente ligada ao Princípio da Responsabilidade Única (S). Se uma classe tem uma responsabilidade clara, sua coesão geralmente é alta. Se uma classe lida com UI, lógica de negócios e rede ao mesmo tempo — a coesão é baixa, e essa classe deve ser dividida em várias classes separadas com responsabilidades mais específicas.
Compreender a coesão ajuda os desenvolvedores a tomar decisões de refatoração. Quando você vê um método em uma classe que não usa nenhum campo da classe, é um sinal de baixa coesão. Esse método está fora do lugar na classe, ou a classe está mal projetada. Buscar alta coesão é um esforço contínuo para melhorar a arquitetura em todos os níveis do código.
Na engenharia de software, distinguem-se sete níveis de coesão, ordenados do pior ao melhor. Compreender esta escala permite avaliar objetivamente a qualidade de um módulo e determinar a direção para refatoração. Quanto maior o nível, mais sustentável e compreensível será o código.
Coincidental — o pior nível, onde os elementos de um módulo são agrupados aleatoriamente sem qualquer conexão lógica. Exemplo: uma classe Utilities com métodos para formatar datas, enviar e-mails e calcular descontos. Essa classe não pode ser entendida sem ler todos os seus métodos, e alterar um pode quebrar outros simplesmente por estarem juntos.
Lógica — os elementos realizam tarefas logicamente relacionadas, mas fundamentalmente diferentes. Uma classe com métodos parseJSON, parseXML e parseCSV está logicamente conectada pelo tópico de “análise,” mas cada método faz um trabalho radicalmente diferente. O problema: ao adicionar um novo formato (YAML), a classe cresce e sua interface fica inchada.
Temporal — os elementos são agrupados por tempo de execução. Uma classe AppInitializer que configura o banco de dados, carrega a configuração e inicializa análises — tudo isso acontece na inicialização do aplicativo, mas as tarefas em si não estão relacionadas. É melhor dividi-las em Initializers separados para cada área de responsabilidade.
Procedural — ocorre quando os elementos são unidos por uma sequência de execução. Um módulo de “processamento de pedidos” contém métodos validateCart, processPayment e sendConfirmation — cada método é chamado estritamente após o anterior. Isso é melhor que a coesão coincidental ou lógica, mas ainda não é ideal: cada etapa pode ser extraída em um módulo separado.
Comunicacional — os elementos trabalham com os mesmos dados. Uma classe UserService com métodos getUser, updateUser e deleteUser está unida pela entidade comum User. Isso é significativamente melhor que a coesão procedural: a classe tem um domínio claro. A maioria das classes Repository em projetos mobile tem coesão comunicacional.
Funcional — o nível mais alto, onde cada elemento do módulo participa na realização de uma única tarefa. Uma classe PasswordValidator com um único método validate que verifica comprimento, presença de caracteres e complexidade da senha é um exemplo de coesão funcional. Se essa classe muda, é apenas porque as regras de validação de senha mudaram.
Alcançar a coesão funcional é o principal objetivo da refatoração arquitetural. Cada classe deve ter exatamente um motivo para mudar. No desenvolvimento mobile, a coesão funcional é alcançada extraindo Use Cases separados, Views personalizadas, formatadores e validadores. Cada uma dessas classes é um bloco de construção completo com uma área de responsabilidade clara.
Coesão e acoplamento são dois lados da mesma qualidade. Quanto maior a coesão dentro de um módulo, menor tende a ser o acoplamento entre módulos. Um sistema bem projetado busca simultaneamente alta coesão interna e acoplamento externo fraco. Este princípio é reconhecido como fundamental na engenharia de software desde a década de 1970.
A relação coesão-acoplamento pode ser entendida como um equilíbrio. Se um desenvolvedor sacrifica a coesão combinando várias tarefas em uma classe, os módulos vizinhos ganham mais dependências — eles precisam acessar esta classe sobrecarregada para diferentes propósitos, o que aumenta o acoplamento. Por outro lado, dividir em classes pequenas e altamente coesas reduz os pontos de interação entre módulos.
Na prática, isso significa: quando você extrai uma nova classe com coesão funcional, você simultaneamente liberta outros módulos da necessidade de conhecer os detalhes de sua implementação. Por exemplo, extrair EncryptionManager em uma classe separada com coesão funcional fornece a outros módulos uma interface simples encrypt/decrypt sem necessidade de entender os detalhes do algoritmo de criptografia.
// Baixa coesão — a classe faz tudo de uma vez
class UserManager {
fun fetchAndSaveUser(id: String) { }
fun parseUserJson(json: String): User { }
fun displayUserName(user: User): String { }
fun validateEmail(email: String): Boolean { }
}
// Alta coesão — cada classe resolve uma tarefa
class UserRepository {
fun fetchUser(id: String): User { }
}
class UserJsonParser {
fun parse(json: String): User { }
}
class UserNameFormatter {
fun format(user: User): String { }
}
class EmailValidator {
fun isValid(email: String): Boolean { }
}
O exemplo mostra a diferença: UserManager tem coesão lógica — todos os métodos são sobre usuários, mas cada um faz um trabalho fundamentalmente diferente. Após a refatoração, cada classe tem coesão funcional e o acoplamento é reduzido porque outros módulos dependem apenas da classe que precisam, não de todo UserManager.
LCOM (Falta de Coesão de Métodos) é a métrica mais conhecida para medir coesão de classe. LCOM conta quantos pares de métodos não compartilham campos comuns. Um valor 0 significa coesão ideal (todos os métodos trabalham com os mesmos campos), enquanto um valor alto indica baixa coesão. LCOM4 (uma versão melhorada) considera conexões transitivas através de outros métodos.
No desenvolvimento Android, métricas de coesão podem ser obtidas através do Detekt com a regra TooManyFunctions. Classes com dezenas de métodos usando diferentes grupos de campos provavelmente têm baixa coesão. No iOS, o SwiftLint tem regras file_length e function_body_length — indicadores indiretos: arquivos e métodos longos frequentemente sinalizam baixa coesão.
Um método de avaliação manual: faça a pergunta “Esta classe mudará por um motivo ou por múltiplos motivos?” Se você pode nomear mais de um motivo independente — a classe tem baixa coesão. Um segundo teste: “Esta classe pode ser dividida em duas classes independentes?” Se sim — faça isso. Verificar regularmente a coesão nas revisões de código previne classes Deus e reduz dívida técnica.
O primeiro passo é aplicar o Princípio da Responsabilidade Única. Cada classe deve ter uma responsabilidade clara. Se uma classe tem um método que não se relaciona com sua tarefa principal, extraia-o para uma classe separada. A técnica Extract Class ou Extract Delegate nas IDEs automatiza esse processo. Após a extração, verifique se a classe original se tornou mais focada.
O segundo passo é usar o padrão Facade para simplificar a interface. Se uma classe fornece 20 métodos, mas os clientes usam apenas 3–4, a classe pode ter baixa coesão — ela oferece funcionalidade muito diversa. Agrupe os métodos por tema, extraia classes separadas para cada grupo e transforme a classe original em uma fachada ou remova-a.
O terceiro passo é prestar atenção aos grupos de campos. Se uma classe tem campos que são usados apenas por um subconjunto de métodos — é um indicador de baixa coesão. Divida a classe por grupos de campos. Por exemplo, se uma classe contém campos userRepository, networkClient e analyticsTracker, mas o primeiro grupo de métodos usa apenas userRepository enquanto o segundo usa networkClient — são duas classes diferentes.
O quarto passo é evitar criar classes “utility” com métodos static arbitrários. Cada método static em uma classe Utils ou Helpers é candidato a ser extraído em uma classe especializada. FormatUtils.dateToString é melhor movido para DateFormatter, e ValidationUtils.isValidEmail para EmailValidator. Isso aumenta a coesão de cada classe e torna o código autodocumentável.
Perguntas frequentes
Quase sempre. A coesão funcional torna o código claro e previsível. No entanto, levá-la ao extremo pode causar fragmentação excessiva: criar uma classe separada para cada operação, tornando a arquitetura excessivamente complexa. O equilíbrio são algumas classes por funcionalidade, cada uma com coesão funcional.
Coesão é uma métrica de consistência interna dentro de um único módulo ou classe. Modularidade é um princípio arquitetural onde uma aplicação é dividida em módulos físicos. Alta coesão é um objetivo tanto ao projetar classes individuais quanto módulos inteiros.
Detekt para Android e Xcode Analyzer para iOS destacam classes com quantidade suspeitamente grande de métodos ou campos. IntelliJ IDEA e AppCode têm visualização de dependências — você pode ver o grafo de conexões e detectar classes com baixa coesão. SonarQube calcula as métricas LCOM automaticamente.
Sim. Uma interface com métodos connect, disconnect e isConnected tem alta coesão — todos os métodos se relacionam com gerenciamento de conexão. Uma interface com connect, parseData e renderUI tem baixa coesão. O Princípio da Segregação de Interfaces (SOLID) exige criar interfaces estreitamente focadas com alta coesão.
Faça três perguntas: O propósito da classe pode ser descrito em uma frase? Todos os métodos apoiam este propósito? Existem campos na classe que não são usados por alguns métodos? Se a resposta a qualquer pergunta for não — a coesão é baixa e a classe deve ser dividida.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também