Diretrizes: Classe de Design
Tópicos
Uma classe de design representa uma
abstração de uma ou várias classes na implementação do sistema; ao que ela
corresponde exatamente depende da linguagem da implementação. Por exemplo, em uma
linguagem orientada a objetos como C++, uma classe pode corresponder a uma classe simples. Em
Ada, uma classe pode corresponder a um tipo rotulado definido na parte visível de um
pacote.
Classes definem objetos que, por sua vez, realizam (implementam) os casos de
uso. A origem de uma classe pode ser tanto os requisitos que as realizações de caso de
uso criam nos objetos necessários do sistema como qualquer modelo de objetos desenvolvido
anteriormente.
O fato de uma classe ser ou não satisfatória dependerá bastante do ambiente de
implementação. O tamanho adequado da classe e de seus objetos depende da linguagem de
programação, por exemplo. O que é considerado certo ao usar Ada pode ser errado ao usar
Smalltalk. As classes devem ser mapeadas para um fenômeno específico da linguagem de
implementação e devem ser estruturadas para que esse mapeamento resulte em um código
satisfatório.
Muito embora as peculiaridades da linguagem de implementação influenciem o modelo de
design, mantenha a estrutura da classe fácil de entender e de modificar. O design deve
ser projetado como se tivesse classes e encapsulamento, mesmo se a linguagem de
implementação não suportar isso.
A única maneira pela qual outros objetos podem obter acesso ou afetar os atributos ou
as relações de um objeto é através de suas operações. As operações de um objeto
são definidas por sua classe. Um comportamento específico pode ser executado por meio das
operações, o que pode afetar os atributos e relacionamentos que o objeto contém e fazer
com que outras operações sejam executadas. Uma operação corresponde a uma função membro
em C++ ou a uma função ou procedimento em Ada. O comportamento que você atribuir a um
objeto dependerá do papel que ele desempenha nas realizações de casos de uso.
Na especificação de uma operação, os parâmetros constituem parâmetros formais. Cada
parâmetro possui um nome e um tipo. É possível usar a sintaxe e a semântica da linguagem
de implementação para especificar as operações e seus parâmetros, para que eles já
estejam especificados na linguagem de implementação quando a codificação for iniciada.
Exemplo:
No Sistema de Máquina de Reciclagem, os objetos de uma classe
Base de Recebimento controlam quantos itens de depósito de um determinado tipo
foram entregues a um cliente. O comportamento de um objeto Base de Recebimento inclui o
incremento do número de objetos retornados. A operação insertItem, que recebe
uma referência ao item entregue, atende a essa finalidade.

Use a sintaxe e a semântica da linguagem de programação ao
especificar operações.
Uma operação quase sempre indica o comportamento do objeto. Uma operação também pode
indicar o comportamento de uma classe e, nesse caso, é uma operação de classe. Isso
pode ser modelado na UML por tipo de escopo da operação.
As seguintes visibilidades são possíveis em uma operação:
- Pública: a operação é visível para modelar elementos que não sejam a
própria classe.
- Protegida: a operação é visível somente para a própria classe, para suas
subclasses ou para amigos da classe (dependente de linguagem)
- Privada: a operação é visível somente para a própria classe e para
amigos da classe
- Implementação: a operação é visível somente dentro da própria classe.
A visibilidade Pública deve ser utilizada muito raramente, apenas
quando uma operação for necessária para uma outra classe.
A visibilidade Protegida deve ser o padrão; ela protege a operação do
uso por classes externas, o que favorece o livre acoplamento e encapsulamento do
comportamento.
A visibilidade Privada deve ser utilizada nos casos em que você deseja evitar
que as subclasses herdem a operação. Isso permite dissociar subclasses da
superclasse e reduzir a necessidade de remover ou excluir operações herdadas não usadas.
A visibilidade Implementação é a mais restritiva; ela é utilizada nos
casos em que apenas a própria classe está apta a utilizar a operação. É uma variante
da visibilidade Privada, que é adequada para a maioria dos casos.
Um objeto pode reagir de maneira diferente a uma determinada mensagem, dependendo do
estado em que está. O comportamento dependente do estado de um objeto é definido por um
diagrama de estados associado. Para cada estado em que o objeto pode entrar, o diagrama
de estados descreve quais mensagens ele pode receber, quais operações serão executadas e
em qual estado o objeto estará a partir dali. Consulte
Diretrizes: Diagrama de Estados para obter informações
adicionais.
Uma colaboração é um conjunto dinâmico de interações de objetos no qual um conjunto
de objetos se comunica enviando mensagens entre si. O envio de mensagens é
realizado diretamente em Smalltalk. Em Ada, ele é realizado como uma chamada de
subprograma. Uma mensagem é enviada para um objeto receptor, que dispara uma operação
dentro do objeto. A mensagem indica o nome da operação a ser executada, juntamente com os
parâmetros necessários. Quando as mensagens são enviadas, parâmetros reais
(valores para os parâmetros formais) são fornecidos para todos os parâmetros.
As transmissões de mensagens entre objetos em uma realização de casos de uso e o foco
de controle que os objetos seguem à medida que as operações são disparadas são descritos
em diagramas de interação. Consulte Diretrizes: Diagrama de
Seqüência e Diretrizes: Diagrama de Comunicação para obter
informações sobre esses diagramas.
Um atributo é uma propriedade nomeada de um objeto. O nome do atributo é um
substantivo que descreve o papel do atributo em relação ao objeto. Um atributo pode ter
um valor inicial quando o objeto é criado.
Só modele atributos se isso tornar um objeto mais compreensível. Você só deve
modelar a propriedade de um objeto como um atributo se for uma propriedade desse
objeto isolado. Caso contrário, modele a propriedade com um relacionamento de
associação ou de agregação com uma classe cujos objetos representem a propriedade.
Exemplo:

Um exemplo de como um atributo é modelado. Cada membro de uma família
possui um nome e um endereço. São identificados aqui os atributos meu nome e
endereço residencial do tipo Nome e Endereço, respectivamente:

Nesse exemplo, uma associação é usada em vez de um atributo. A
propriedade meu nome provavelmente é exclusiva para cada membro de uma
família. Portanto, é possível modelá-la como um atributo do tipo Nome. Um
endereço, no entanto, é compartilhado por todos os membros da família, assim é melhor
modelado por uma associação entre as classes Membro da Família e Endereço.
Nem sempre é fácil decidir imediatamente se um conceito deve ser modelado como um
objeto separado ou como um atributo de um outro objeto. Ter objetos desnecessários no
modelo leva à documentação desnecessária e à sobrecarga no desenvolvimento. Portanto, é
preciso estabelecer certos critérios para determinar a importância de um conceito para o
sistema.
- Acessibilidade. O que direciona a escolha de um objeto e não de um atributo
não é a importância do conceito na vida real, mas a necessidade de acessá-lo durante o
caso de uso. Se a unidade for acessada com freqüência, modele-a como um objeto.
- Separação durante a execução. Modele conceitos tratados separadamente
durante a execução de casos de uso como objetos.
- Vínculos para outros conceitos. Modele conceitos estritamente vinculados a
outros determinados conceitos e nunca usados separadamente, mas sempre através de um
objeto, como um atributo do objeto.
- Demandas de relações. Se, por algum motivo, você precisar relacionar
uma unidade a partir de duas direções, examine a unidade novamente para ver se ela deve
ser um objeto separado. Dois objetos não podem se associar à mesma instância de um tipo
de atributo.
- Freqüência de ocorrência. Se existir uma unidade somente durante um caso de
uso, não a modele como um objeto. Em vez disso, modele-a como um atributo para o objeto
que executa o comportamento em questão ou simplesmente a mencione na descrição do objeto
afetado.
- Complexidade. Se um objeto se tornar complicado demais por causa de seus
atributos, talvez você consiga extrair alguns dos atributos para objetos separados. Faça
isso com moderação, no entanto, para não ter objetos demais. Por outro lado, as unidades
podem ser muito simples. Por exemplo, são classificadas como atributos (1) unidades
suficientemente simples para serem suportadas diretamente por tipos primitivos na
linguagem de implementação, como inteiros em C++, e (2) unidades suficientemente simples
para serem implementadas utilizando os componentes independentes de aplicativos do
ambiente de implementação, como uma Cadeia em C++ e Smalltalk-80.
É muito provável que você modele um conceito de maneira diferente para sistemas
diferentes. Em um sistema, o conceito pode ser tão vital que você irá modelá-lo como um
objeto. Já em outro sistema, pode ser que ele seja de menor importância, e então você o
modelará como um atributo de um objeto.
Exemplo:
Por exemplo, para uma empresa aérea, você desenvolveria um sistema que
suporta partidas.

Um sistema que suporta partidas. Suponha que os funcionários que
trabalham em um aeroporto desejem um sistema que suporte partidas. Para cada partida,
defina o horário da partida, a empresa aérea e o destino. Você pode modelar isso como um
objeto de uma classe Partida, com os atributos horário de partida,
empresa aérea e destino.
Se, em vez disso, o sistema for desenvolvido para uma agência de
viagens, a situação pode ser um pouco diferente.

Os destinos dos vôos formam seu próprio objeto, Destino.
É claro que o horário de partida, a empresa aérea e o destino ainda
serão necessários. Mesmo assim, outros requisitos precisam ser adicionados, pois uma
agência de viagens está interessada em localizar uma partida com um destino específico. Sendo
assim, crie um objeto separado para Destino. Obviamente, os objetos de
Partida e de Destino precisam estar cientes um do
outro, o que é permitido por uma associação entre suas classes.
O argumento para a importância de certos conceitos também é válido para determinar
quais atributos devem ser definidos em uma classe. Se seus objetos fizerem parte de um
sistema de registro de veículos motorizados, não há dúvida de que a classe
Carro irá definir atributos diferentes dos que faria se eles fizessem
parte de um sistema de fabricação de automóveis.
Por fim, as regras para o que representar como objetos e o que representar como
atributos não são absolutas. Teoricamente, é possível modelar tudo como objetos, mas fica
pesado. Uma regra prática simples é ver um objeto como algo que, em algum estágio, será
usado sem levar em consideração outros objetos. Além disso, não é necessário modelar
todas as propriedades de objeto usando um atributo, somente as propriedades necessárias
para compreender o objeto. Você não deve modelar detalhes que sejam tão específicos da
implementação que serão melhor manipulados pelo implementador.
Atributos
de Classe
Um atributo quase sempre indica propriedades do objeto. Um atributo pode também
denotar as propriedades de uma classe e, nesse caso, ele seria um atributo de
classe. Isso pode ser modelado na UML por tipo de escopo do atributo.
Um objeto pode encapsular algo cujo valor pode ser alterado sem que o objeto execute
qualquer comportamento. Pode ser algo que seja realmente uma unidade externa, mas que não
tenha sido modelado como um ator. Por exemplo, as fronteiras do sistema podem ter sido
escolhidas para que algum tipo de equipamento sensor fique dentro delas. O sensor pode
então ser encapsulado dentro de um objeto, de modo que o valor que ele mede constitua um
atributo. Esse valor pode ser alterado continuamente ou em intervalos determinados, sem
que o objeto seja influenciado por qualquer outro objeto do sistema.
Exemplo:
É possível modelar um termômetro como um objeto. O objeto possui um
atributo que representa temperatura e altera o valor em resposta a mudanças na
temperatura do ambiente. Outros objetos podem pedir a temperatura atual ao executar uma
operação no objeto termômetro.

O valor do atributo temperatura muda espontaneamente no
objeto Termômetro.
Um valor encapsulado que é alterado dessa forma pode ainda ser modelado como um
atributo comum, mas deve ser descrito na classe do objeto que ele muda espontaneamente.
A visibilidade do atributo assume um dos seguintes valores:
- Público: o atributo é visível dentro e fora
do pacote que contém a classe.
- Protegido: o atributo é visível somente para a própria classe, para suas
subclasses ou para amigos da classe (dependente de linguagem)
- Privado: o atributo é visível somente para a própria classe e para
amigos da classe
- Implementação: o atributo é visível para a própria classe.
A visibilidade Pública deve ser utilizada muito raramente, apenas
quando um atributo puder ser acessado diretamente por uma outra classe. A definição da
visibilidade pública é, na verdade, uma notação abreviada para definir a visibilidade do
atributo como protegida, privada ou de implementação, com operações públicas associadas
para obter e definir o valor do atributo. A visibilidade pública do atributo pode ser
usada como uma declaração para um gerador de código de que essas operações obter/definir
devem ser geradas automaticamente, economizando tempo durante a definição da classe.
A visibilidade Protegida deve ser o padrão; ela protege o atributo do
uso por classes externas, o que favorece o livre acoplamento e encapsulamento do
comportamento.
A visibilidade Privada deve ser utilizada nos casos em que você deseja evitar
que as subclasses herdem o atributo. Isso permite dissociar subclasses da
superclasse e reduzir a necessidade de remover ou excluir atributos herdados não usados.
A visibilidade Implementação é a mais restritiva; ela é utilizada nos
casos em que apenas a própria classe está apta a utilizar o atributo. É uma variante
da visibilidade Privada, que é adequada para a maioria dos casos.
Algumas classes podem representar abstrações complexas e ter uma estrutura complexa. Ao
modelar uma classe, talvez o designer prefira representar suas relações e elementos
internos participantes, para ter certeza de que o implementador irá implementar
adequadamente as colaborações que ocorrem nessa classe.
No UML 2.0, as classes são definidas como
classes estruturadas, com capacidade
para ter portas e uma estrutura interna. Dessa forma, as classes podem ser decompostas em
coleções de partes conectadas que, por sua vez, podem ser posteriormente decompostas. Uma
classe pode ser encapsulada, forçando a passagem das comunicações pelo lado externo
através de portas que obedecem a interfaces declaradas.
Nesse caso, além de utilizar diagramas de classe para representar relações de
classes (por exemplo, associações, composições e agregações) e atributos, o designer
pode querer utilizar um diagrama de estrutura composto. Esse diagrama fornece ao
designer um mecanismo para mostrar como as instâncias das partes internas desempenham
suas funções na instância de uma determinada classe.
Para obter informações adicionais sobre este tópico e exemplos sobre o diagrama de
estrutura composto, consulte
Conceitos: Classe Estruturada.
|