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