Diretriz: Classe de Design
Uma Classe de Design representa um elemento de design que será mapeado diretamente para o código. Esta orientação explica como desenvolver uma Classe de Design.
Relacionamentos
Descrição Principal

Definição

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.

Operações

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.

Parâmetros

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.

Diagrama descrito no texto associado.

Use a sintaxe e a semântica da linguagem de programação ao especificar operações.

Operações de Classe

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.

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

Estados

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.

Colaborações

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:

Diagrama descrito no texto associado.

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:

Diagrama descrito no texto associado.

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.

Diagrama descrito no texto associado.

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.

Diagrama descrito no texto associado.

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.

Diagrama descrito no texto associado.

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.

Visibilidade do Atributo

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.

Estrutura Interna

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.