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 esse propósito.
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 Técnica:
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 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 Técnica: Diagrama de Seqüência e Técnica:
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 home 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 manipulados 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, que é ativado 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 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 agente. 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 é alterado 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 é alterado 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.
-
Protegida: o atributo é visível somente para a própria classe, para suas subclasses ou para amigos da
classe (dependente de linguagem)
-
Privada: 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.
Na UML 2.0, as classes são definidas como classes
estruturadas, com o recurso 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 composta, consulte Conceito: Classe Estruturada.
|