Diretrizes: Pacote de Design
Tópicos
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 Artefato: Subsistema
de Design e Diretrizes: Subsistema de Design. Por outro
lado, os pacotes de design são apenas para agrupamento.
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.
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.
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.

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.

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.
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 seja 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
Há 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 das duas classes de controle.

Administrador de Pedidos e Registrador de
Pedidos pertencem ao mesmo pacote que 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 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
Plataforma de Carga, no Sistema de Manipulação de Depósito, apresenta
uma instância da classe de entidade Plataforma de Carga para o usuário. Cada Plataforma de
Carga é representada por um número de identificação na tela. Se as
informações sobre uma Plataforma de Carga forem alteradas, por exemplo, se a Plataforma de Carga
também receber um nome, é provável que a classe de limite também tenha que ser alterada. Portanto, Formulário
de Plataforma de Carga deve ser incluído no mesmo pacote que Plataforma de Carga.
- Duas classes podem estar relacionadas funcionalmente se forem afetadas por mudanças no mesmo ator ou se interagirem com ele.
Se as duas classes não envolverem o mesmo ator, 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 ator Transportador, o transportador físico que pode transportar
uma plataforma de carga no depósito. O ator 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.

Interface do Transportador e Executor de
Tarefas pertencem ao mesmo pacote porque ambos são afetados
por mudanças no ator 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.
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 ator 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 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'.

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''.
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.

O pacote Manipulação de Pedidos depende
de Manipulação de Itens, porque existe uma associação entre duas
classes nos 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.

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.

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.
|