Como mostra o Exemplo 8, os desenvolvedores precisam lidar com as dependências que surgem como resultado da decomposição de um problema e sua solução em vários módulos. Dizemos que um módulo de um sistema depende de outro se é possível que uma mudança para um módulo requeira uma mudança para outro. Por exemplo, se uma empresa muda seus métodos de produção, isso pode causar uma mudança conseqüente na forma como calcula os pagamentos necessários para os bens que produz.
Um desenvolvedor deve lidar não só com a natureza de cada dependência, mas também com o número de dependências. Na engenharia de software, ‘acoplamento’ é usado para se referir ao grau de interdependência entre as diferentes partes de um sistema. É fácil ver que certos sistemas podem ter cadeias de módulos interdependentes onde, por exemplo, o módulo A depende do módulo B, que depende do módulo C, e assim por diante. Em alguns casos essas cadeias podem se unir e criar uma dependência circular, que é uma forma particular de acoplamento forte (ou alto).
Desenvolvedores tentam construir sistemas de acoplamento frouxo porque são mais fáceis de entender e manter. Assim um bom sistema de software tem baixo acoplamento, o que significa que as mudanças em uma parte são menos propensas a se propagar através do resto do sistema. Um outro benefício do baixo acoplamento é que os componentes são fáceis de substituir e, potencialmente, reutilizar.
Sejam quais forem os níveis de acoplamento em um sistema de software, é importante saber quais módulos estão acoplados. Se não houvesse registros do acoplamento entre módulos, um desenvolvedor teria que gastar tempo trabalhando através dos módulos para determinar se cada um deles foi ou não afetado por uma mudança. O resultado seria um grande esforço gasto na verificação, mesmo que não fossem necessárias alterações. O exemplo 9 ilustra o perigo de ter mais de um módulo fazendo uso de dados comuns ou compartilhados.
Exemplo 9
A manipulação de datas sempre foi um problema para os desenvolvedores de software. Para aplicações de uma certa idade, o formato de armazenamento mais aplicável para representar um ano era um número entre 0 e 99. Fazia sentido porque 1966 foi armazenado como 66, 1989 como 89, e assim por diante, portanto menos espaço foi necessário para armazenar apenas dois dígitos. Além disso, se as datas eram armazenadas como números, as tarefas que envolviam ordenação por data eram simples – 22 de Janeiro de 1989 armazenado como 890122, é após 22 de Dezembro de 1966 armazenado como 661222.
Felizmente, algumas dessas aplicações ainda estavam em uso à medida que o ano 2000 se aproximava, então cada módulo em cada aplicação que utilizava a forma curta do ano tinha que ser investigado.
Um aspecto importante do problema no Exemplo 9 era que diferentes desenvolvedores tinham diferentes formas de ler e manipular os valores armazenados em variáveis que utilizavam o formato de seis dígitos de data. Isto aumentou o esforço necessário para resolver o chamado bug do milênio. Se os desenvolvedores tivessem tido uma maneira consistente de manipular datas que não dependessem do formato de armazenamento, o bug do milênio não teria sido um problema preocupante.
Cohesion é uma maneira de descrever quão intimamente as atividades dentro de um único módulo estão relacionadas umas com as outras. Coesão é um conceito geral – por exemplo, um departamento de uma organização pode ter um conjunto coeso de responsabilidades (contas, digamos), ou não (serviços diversos). Em sistemas de software, um módulo altamente coeso realiza uma tarefa ou atinge um único objectivo – “fazer uma coisa e fazê-lo bem” é um lema útil a aplicar. Um módulo deve implementar uma única tarefa lógica ou uma única entidade lógica.
Baixo acoplamento e alta coesão são metas concorrentes. Se cada módulo faz apenas uma coisa a um baixo nível de abstração, podemos precisar de uma construção complexa de módulos altamente acoplados para realizar uma atividade a níveis mais altos de abstração. Um desenvolvedor deve tentar alcançar o melhor equilíbrio entre os níveis de acoplamento e coesão para um sistema de software. Por exemplo, hotéis geram renda ao alugarem seus quartos aos hóspedes. É provável que o conceito de quarto seja representado em algum lugar no sistema de software para reservas de um hotel. Pode ser conveniente usar um módulo ou classe representando o conceito de quarto para coletar e armazenar dados sobre a renda gerada pela locação de quartos. No entanto, uma solução melhor é ter uma conta ou módulo de pagamento separado, porque é mais coeso, especialmente quando um hotel gera renda de outras formas, por exemplo, servindo refeições a pessoas que não são hóspedes residentes.
Actividade 5 Dividir e conquistar
- a.Por que você poderia considerar dividir um grande projeto em pedaços menores?
- b.Como a complexidade de um sistema de software afeta a tarefa de manutenção?
- c.O que é um módulo?
- d.Porque é que ajuda ter um baixo acoplamento num sistema de software?
- e.Dar exemplos dos tipos de informação que seriam valiosos quando se considera uma alteração a um determinado módulo.
- f.Quais são as dependências de contexto de um módulo? Como elas se relacionam com a interface de um módulo?
- g.Quais são os benefícios de usar módulos com interfaces definidas?
- h.Por que ajuda ter alta coesão nos módulos de um sistema de software?
- i.Que características deve apresentar um módulo que ajude a garantir que é fácil e barato de desenvolver e manter, e que os erros são mantidos a um mínimo?
- j.Porque é importante alcançar um equilíbrio entre acoplamento e coesão?
Resposta
- a.Há um limite para o quanto uma pessoa pode entender a qualquer momento. Portanto, há um limite para o tamanho de um sistema de software com o qual qualquer pessoa pode lidar. Dividindo um grande projeto em pedaços menores, é possível identificar um número de tarefas mais gerenciáveis para os envolvidos.
- b.É essencial ser capaz de fazer uma mudança em um sistema de software sem ter que saber tudo sobre esse sistema. Cada mudança se torna difícil quando o fluxo de controle e dependências dentro dos programas são complexos. Quanto maior o número e a natureza das dependências, mais difícil é manter um sistema de software.
- c.Um módulo é qualquer parte identificável de um sistema de software que é considerado separadamente. Por exemplo, os módulos podem ser subrotinas (em uma linguagem de procedimento equivalente a métodos), classes (em uma linguagem orientada a objetos), funções de biblioteca ou outras construções que podem ser tratadas independentemente.
- d.Com baixo acoplamento, há poucas dependências entre os módulos. Portanto, alterações feitas em uma parte (um ou mais módulos) de um sistema de software são menos propensos a se propagar por todo o sistema. (Um registro claro das dependências entre módulos ajuda a prever o impacto de uma alteração proposta a um sistema de software.)
- e.Existem dois tipos de informação que contribuem para a análise de uma alteração proposta:
- Quais são os módulos clientes do módulo em questão? Esta informação indica até que ponto uma mudança pode propagar-se através do sistema de software.
- Que suposições foram feitas nos módulos clientes do módulo em questão? Um entendimento dos serviços esperados de um módulo ajudará a avaliar os riscos associados a uma determinada mudança.
- f.As dependências de contexto para um módulo são os serviços de outros módulos que o módulo precisa para funcionar corretamente. Você pode expressar as dependências de contexto de um módulo em termos de outras interfaces. Na verdade, é possível expressar as responsabilidades de um módulo em termos de sua interface e dependências de contexto. Se o contexto fornece os serviços que o módulo precisa e os clientes satisfazem quaisquer condições especificadas na interface, o módulo pode garantir o fornecimento dos serviços descritos na sua interface.
- g.Os benefícios são os seguintes:
- Desenvolvedores precisarão saber apenas sobre a interface do módulo (sua sintaxe e o que ele requer e alcança – sua semântica), não como ele fornece esses serviços. Consequentemente os desenvolvedores podem ser mais produtivos.
- Desenvolvedores podem entender aspectos do sistema de software mais completamente, assim menos bugs serão introduzidos.
- Deve ser mais fácil encontrar bugs, pois módulos irrelevantes são evitados.
- A possibilidade de reutilização do módulo é aumentada uma vez que se sabe o que o módulo fornece e requer.
- h.Com alta coesão, um módulo realiza um conjunto sensato de operações ou atividades. Idealmente, uma alta coesão implica apenas uma grande abstracção por módulo. A interface abstrai longe do que um desenvolvedor deve saber para poder usar um módulo. Isto facilita aos desenvolvedores entender o propósito do módulo e como usá-lo. Além disso, a alta coesão tende a tornar um módulo mais reutilizável em outras aplicações, pois ele fornece um conjunto de operações que ficam naturalmente juntas.
- i.Um módulo deve ter baixo acoplamento e alta coesão, representar uma boa abstração, e ter uma interface bem definida que é uma abstração encapsulada de um conceito bem compreendido.
- j.Ao construir um sistema, você pode ter a escolha entre um conjunto menor de módulos frouxamente acoplados, menos coesivos, ou um conjunto maior de módulos firmemente acoplados, mais coesivos. No primeiro caso, cada módulo pode ser difícil de entender, enquanto no segundo caso as relações entre eles podem ser muito complexas. Você precisa atingir um equilíbrio apropriado.