Diretriz: Pacote de Design
Um Pacote de Design é um constructo utilizado para particionar o modelo de design. Esta diretriz explica como identificar e especificar Pacotes de Design.
Relacionamentos
Descrição Principal

Introdução

O Modelo de Design pode ser estruturado em unidades menores para que seja mais fácil de ser compreendido. O agrupamento de elementos do Modelo de Design em pacotes e subsistemas, mostrando como esses agrupamentos estão relacionados entre si, é um procedimento que facilita a compreensão da estrutura geral do modelo. Observe que um subsistema de design é modelado como um componente que realiza uma ou mais interfaces; para obter informações adicionais, consulte Produto de Trabalho: Subsistema de Design e Diretriz de Produto de Trabalho: Subsistema de Design. Por outro lado, os pacotes de design são apenas para agrupamento.

Visibilidade do Conteúdo do Pacote

Uma classe contida em um pacote pode ser pública ou privada. Uma classe pública pode ser associada por qualquer outra classe. Uma classe privada pode ser associada apenas por classes contidas no pacote.

A interface de um pacote consiste nas classes públicas do pacote. A interface do pacote (classes públicas) isola e implementa as dependências existentes em outros pacotes. Isso simplifica o desenvolvimento paralelo, pois permite estabelecer interfaces com antecedência e os desenvolvedores só precisam saber das mudanças feitas nas interfaces de outros pacotes.

Critérios de Particionamento do Pacote

Há vários motivos para você particionar o Modelo de Design:

  • Os pacotes e os subsistemas podem ser usados como unidades de liberação, configuração ou pedido depois que o sistema estiver concluído.
  • A alocação de recursos e a competência de diferentes equipes de desenvolvimento podem exigir que o projeto seja dividido entre vários grupos em locais distintos. Os subsistemas, com interfaces bem definidas, possibilitam a divisão do trabalho entre equipes de maneira controlada e coordenada, enquanto o design e a implementação prosseguem em paralelo.
  • Os subsistemas podem ser usados para estruturar o modelo de design de forma a refletir os tipos de usuário. A origem de muitas mudanças de requisitos são os usuários. Os subsistemas garantem que as mudanças feitas por determinado tipo de usuário afetarão somente as partes do sistema correspondentes a esse tipo de usuário.
  • Em alguns aplicativos, determinadas informações devem estar acessíveis somente para poucas pessoas. Os subsistemas permitem a manutenção do sigilo do usuário em áreas onde isso é necessário.
  • Se estiver criando um sistema para suporte, você poderá usar subsistemas e pacotes para dar a ele uma estrutura semelhante à do sistema a ser suportado. Assim será possível sincronizar a manutenção dos dois sistemas.
  • Os subsistemas são utilizados para representar os produtos e serviços existentes utilizados pelo sistema (por exemplo, bibliotecas e produtos COTS), conforme explicado nas próximas seções.

Classes de Limite de Pacote

Quando as classes de fronteira são distribuídas nos pacotes, duas estratégias diferentes podem ser aplicadas. A escolha da estratégia depende se as interfaces do sistema estão propensas a grandes mudanças no futuro.

  • Se houver a probabilidade de a interface do sistema ser substituída ou sofrer mudanças consideráveis, a interface deverá ser separada do resto do modelo de design. Quando a interface do usuário for alterada, somente esses pacotes serão afetados. Um exemplo desse tipo de mudança é trocar uma interface orientada por linhas por outra orientada por janelas.

Diagrama descrito no texto associado.

Se o objetivo principal é simplificar as principais mudanças de interface, as classes de fronteira devem ser colocadas em um ou vários pacotes separados.

  • Caso não haja nenhuma grande mudança de interface planejada, as mudanças efetuadas nos serviços do sistema devem ser o princípio orientador. As classes de fronteira devem então ficar juntas com as classes de controle e de entidade com as quais elas estão relacionadas funcionalmente. Dessa forma, será fácil verificar quais classes de fronteira serão afetadas se uma classe de controle ou de entidade for alterada.

Diagrama descrito no texto associado.

Para simplificar as mudanças efetuadas nos serviços do sistema, as classes de fronteira são empacotadas com as classes às quais estão relacionadas funcionalmente.

As classes de limite obrigatórias, que não estejam funcionalmente relacionadas a nenhuma classe de entidade ou de controle, devem ser colocadas em pacotes separados, juntamente com as classes de limite pertencentes à mesma interface.

Se uma classe de fronteira estiver relacionada a um serviço opcional, agrupe-a com as classes que colaboram para oferecer o serviço em um subsistema separado. O subsistema fará o mapeamento para um componente opcional, que será fornecido quando a funcionalidade opcional for solicitada.

Empacotando Classes Funcionalmente Relacionadas

Um pacote deve ser identificado para cada grupo de classes relacionadas funcionalmente. Diversos critérios práticos podem ser aplicados para avaliar se duas classes estão relacionadas funcionalmente. São eles (por ordem decrescente de importância):

  • Se as mudanças efetuadas na estrutura e/ou no comportamento de uma classe exigirem mudanças em outra classe, as duas classes estão relacionadas funcionalmente.

Exemplo

Se um novo atributo for incluído na classe de entidade Pedido, é muito provável que a classe de controle Administrador de Pedidos precise ser atualizada. Portanto, elas pertencem ao mesmo pacote, Manipulação de Pedidos.

  • Para saber se uma classe está relacionada funcionalmente a outra, comece com uma classe de entidade, por exemplo, e verifique qual será o impacto de removê-la do sistema. As classes que se passarem a ser supérfluas após a remoção da outra classe estão, de alguma forma, conectadas à classe removida. Definimos como supérflua a classe que só é usada pela classe removida ou que depende da classe removida.

Exemplo

Existe um pacote Manipulação de Pedidos que contém as duas classes de controle, Administrador de Pedidos e Registrador de Pedidos, no Sistema de Manipulação de Depósito. As duas classes de controle modelam serviços referentes ao gerenciamento de pedidos feitos ao depósito. Todos os atributos e relacionamentos dos pedidos são armazenados pela classe de entidade Pedido, que só existe para a manipulação de pedidos. Se a classe de entidade for removida, não haverá necessidade de manter o Administrador de Pedidos ou o Registrador de Pedidos, porque eles serão úteis apenas se a classe Pedido existir. Por isso, a classe de entidade Pedido deve ser incluída no mesmo pacote que as duas classes de controle.

Diagrama descrito no texto associado.

O Administrador de Pedidos e o Registrador de Pedidos pertencem ao mesmo pacote que o Pedido, pois se tornarão supérfluos se Pedido for removido do sistema.

  • Dois objetos podem estar relacionados funcionalmente se interagirem por meio de um grande número de mensagens ou se, pelo contrário, tiverem um sistema de intercomunicação complicado.

Exemplo

A classe de controle Executor de Tarefas envia e recebe muitas mensagens para/da Interface do Transportador. Essa é uma outra indicação de que elas devem ser incluídas no mesmo pacote, Manipulação de Tarefas.

  • Uma classe de fronteira pode estar relacionada funcionalmente a determinada classe de entidade se a função da classe de fronteira for apresentar a classe de entidade.

Exemplo

A classe de limite, Formulário de Palete, no Sistema de Manipulação de Depósito, apresenta uma instância da classe de entidade Palete para o usuário. Cada Palete é representado por um número de identificação na tela. Se as informações sobre um Palete forem alteradas, por exemplo, se o Palete também receber um nome, é provável que a classe de limite também tenha que ser alterada. Portanto, Formulário de Palete deve ser incluído no mesmo pacote que Palete.

  • Duas classes podem estar relacionadas funcionalmente se forem afetadas por mudanças no mesmo agente ou se interagirem com ele. Se as duas classes não envolverem o mesmo agente, elas não devem estar no mesmo pacote. Obviamente, a última regra poderá ser ignorada se houver motivos mais importantes.

Exemplo

Há um pacote Manipulação de Tarefas no Sistema de Manipulação de Depósito que inclui, entre outras coisas, a classe de controle Executor de Tarefas. Este é o único pacote envolvido com o agente Transportador, o transportador físico que pode transportar um palete no depósito. O agente interage com a classe de controle Executor de Tarefas por meio da classe de limite Interface do Transportador. Portanto, essa classe de limite deve ser incluída no pacote Manipulação de Tarefas.

Diagrama descrito no texto associado.

Interface do Transportador e Executor de Tarefas pertencem ao mesmo pacote porque ambos são afetados por alterações no agente Transportador.

  • Duas classes podem estar relacionadas funcionalmente se houver algum relacionamento entre elas (associações, agregações, etc.). Obviamente, esse critério não pode ser seguido sem um certo cuidado, mas pode ser adotado quando nenhum outro critério for aplicável.
  • Uma classe pode estar relacionada funcionalmente à classe que cria instâncias da primeira.

Esses dois critérios determinam quando duas classes não devem ser colocadas no mesmo pacote:

  • Duas classes que estejam relacionadas a atores diferentes não devem ser colocadas no mesmo pacote.
  • Uma classe opcional e uma classe obrigatória não devem ser colocadas no mesmo pacote.

Avaliando a Coesão do Pacote

Primeiramente, todos os elementos de um pacote devem ter a mesma opcionalidade: não pode haver elementos de modelos opcionais em um pacote obrigatório.

Exemplo

A classe de entidade obrigatória Tipo de Artigo tem, entre outras coisas, um atributo denominado Limite de Rearmazenamento. A função de rearmazenamento, contudo, é opcional no sistema. Portanto, Artigo deve ser dividido em duas classes de entidade, em que a classe opcional está relacionada à obrigatória.

Um pacote considerado obrigatório pode não depender de qualquer pacote considerado opcional.

Como regra, um único pacote não pode ser usado por dois atores diferentes. O motivo para isso é que uma mudança no comportamento de um agente não deve afetar os outros atores. Há exceções a essa regra, como é o caso de pacotes que constituem serviços opcionais. Pacotes desse tipo não devem ser divididos, seja qual for o número de atores a utilizá-lo. Por isso, divida qualquer pacote (ou classe) que seja utilizado por diversos atores, a menos que o pacote seja opcional.

Todas as classes contidas no mesmo pacote devem estar relacionadas funcionalmente. Se você adotou os critérios descritos na seção "Localizar Pacotes de Classes Funcionalmente Relacionadas", as classes de um pacote estarão funcionalmente relacionadas entre si. Contudo, uma determinada classe pode conter, em si mesmo, comportamentos ou relacionamentos "excessivos" que não pertençam à classe. Parte da classe deverá ser removida para se transformar em uma classe completamente nova ou em alguma outra classe, que provavelmente pertencerá a um outro pacote.

Exemplo

O comportamento de uma classe de controle A, contida em um pacote, não deve depender excessivamente de uma classe B, contida em outro pacote. Para isolar o comportamento específico de B, a classe de controle A deve ser dividida em duas classes de controle, A' e A". O comportamento específico de B é colocado na nova classe de controle, A", que é colocada no mesmo pacote que B. A nova classe A" também obtém um relacionamento, como generalização, com o objeto original A'.

Diagrama descrito no texto associado.

Para isolar o comportamento específico de B, a classe de controle A, que necessita de homogeneidade, é dividida em duas classes de controle, A' e A''.

Descrevendo Dependências de Pacotes

Se uma classe contida em um pacote estiver associada a uma classe em outro pacote, esses pacotes dependerão um do outro. As dependências de pacotes são modeladas usando o relacionamento de dependência existente entre os pacotes. Os relacionamentos de dependência nos ajudam a avaliar a conseqüência das mudanças: um pacote do qual muitos pacotes dependam é mais difícil de ser alterado do que um pacote que não possua dependências.

Como várias dependências desse tipo serão descobertas durante a especificação dos pacotes, esses relacionamentos deverão passar por mudanças durante o trabalho. A descrição de um relacionamento de dependência deve incluir informações sobre quais relacionamentos de classe causaram a dependência. Como esse procedimento apresenta informações difíceis de serem mantidas, ele só deve ser adotado se as informações forem pertinentes e importantes.

Exemplo

No Sistema de Manipulação de Depósito, há um relacionamento de dependência do pacote Manipulação de Pedidos com o pacote Manipulação de Itens. Esta associação surge porque a classe de entidade Pedido em Manipulação de Pedidos possui uma associação com a classe de entidade Tipo de Item no outro pacote.

Diagrama descrito no texto associado.

O pacote Manipulação de Pedidos depende de Manipulação de Itens, porque existe uma associação entre duas classes nos pacotes.

Avaliando o Acoplamento de Pacotes

O acoplamento de pacotes é bom e ruim: bom porque acoplamento representa reutilização e ruim porque acoplamento representa dependências que dificultam a alteração e a evolução do sistema. Alguns princípios gerais podem ser seguidos:

  • Os pacotes não devem ser acoplados entre si (ou seja, não devem ser co-dependentes). Um pacote não deve depender de outro.

Diagrama descrito no texto associado.

Nesses casos, os pacotes precisam ser reorganizados para remover as interdependências.

  • Os pacotes de camadas inferiores não devem depender dos pacotes de camadas superiores. Só deve haver dependência entre pacotes que estejam na mesma camada e na camada inferior seguinte.

Diagrama descrito no texto associado.

Nesses casos, a funcionalidade precisa ser reparticionada. Uma solução é definir as dependências em termos de interfaces e organizar as interfaces na camada inferior.

  • Em geral, as dependências não devem ignorar as camadas, a menos que o comportamento dependente seja comum em todas as camadas. A alternativa é deixar as chamadas de operações atravessarem as camadas.
  • Pacotes não devem depender de subsistemas, somente de outros pacotes ou de interfaces.