Tarefa: Design da Classe
Essa tarefa define como projetar a estrutura de classe de um subsistema ou componente.
Disciplinas: Análise e Design
Objetivo
  • Garantir que a classe forneça o comportamento exigido pelas realizações de casos de uso
  • Garantir que informações suficientes sejam fornecidas para implementar a classe sem qualquer ambigüidade
  • Tratar os requisitos não funcionais relacionados à classe
  • Incorporar os mecanismos de design utilizados pela classe
Relacionamentos
Descrição Principal

As classes são responsáveis por impulsionar o esforço de design , ou seja, são elas que de fato executam o trabalho real do sistema. Outros elementos de design, como subsistemas, pacotes e colaborações, descrevem como as classes são agrupadas ou como interoperam.

As cápsulas também são classes estereotipadas, usadas para representar encadeamentos de execução simultânea em sistemas de tempo real. Em tais casos, outras classes de design são classes passivas, utilizadas no contexto de execução fornecido pelas cápsulas ativas. Quando o arquiteto de software e o designer escolhem não utilizar uma abordagem de design com base em cápsulas, ainda é possível modelar comportamentos simultâneos utilizando classes ativas.

Classes ativas são classes de design que coordenam e conduzem o comportamento das classes passivas - uma classe ativa é uma classe cujas instâncias são objetos ativos, que possuem seu próprio encadeamento de controle.

Etapas
Utilizar Padrões e Mecanismos de Design

Utilize padrões e mecanismos de design conforme adequado à classe ou ao recurso que está sendo projetado e de acordo com as diretrizes de design do projeto.

A incorporação de um padrão e/ou mecanismo é executar com eficiência muitas das etapas subseqüentes desta tarefa (incluindo novas classes, operações, atributos e relações), mas de acordo com as regras definidas pelo padrão ou mecanismo.

Observe que padrões e mecanismos normalmente são incorporados conforme a evolução do design e não apenas como a primeira etapa desta tarefa. Além disso, eles são freqüentemente aplicados a um conjunto de classes, e não apenas a uma única classe.

Criar Classes de Design Inicial

Crie uma ou várias classes de design inicial para a classe de análise determinada como entrada para esta tarefa e designe dependências de rastreio. As classes de design criadas nesta etapa serão refinadas, ajustadas, divididas ou mescladas nas etapas subseqüentes, quando forem designadas várias propriedades de design - como operações, métodos e uma máquina de estado - que descrevem como a classe de análise é projetada.

Dependendo do tipo da classe de análise (limite, entidade ou controle) que está sendo projetada, há estratégias específicas que podem ser utilizadas para criar classes de design inicial.

Projetando Classes de Limite

Classes de limite representam interfaces para usuários ou para outros sistemas.

Normalmente, classes de limite que representam interfaces para outros sistemas são modeladas como subsistemas, porque muitas vezes possuem comportamento interno complexo. Se o comportamento da interface for simples (talvez agindo apenas como uma passagem para uma API existente para o sistema externo), será possível optar por representar a interface com uma ou mais classes de design. Se essa for a sua escolha, utilize uma única classe de design por protocolo, interface ou API e observe os requisitos especiais sobre padrões utilizados nos requisitos especiais da classe.

Classes de limite que representam interfaces para usuários geralmente seguem a regra de uma classe de limite para cada janela ou uma para cada formulário, na interface com o usuário. Conseqüentemente, as responsabilidades das classes de limite podem estar em um nível razoavelmente alto, sendo necessário serem refinadas e detalhadas nesta etapa. Modelos adicionais ou protótipos da interface com o usuário podem ser outra origem de entrada a ser considerada nesta etapa.

O design das classes de limite depende das ferramentas de desenvolvimento da UI (interface com o usuário) disponíveis para o projeto. Com o uso da tecnologia atual, é comum que a UI seja construída de forma visível diretamente na ferramenta de desenvolvimento. Isso cria automaticamente as classes da UI que precisam ser relacionadas ao design das classes de controle e de entidade. Se o ambiente de desenvolvimento da UI criar automaticamente as classes de suporte necessárias para implementar a UI, não haverá necessidade de considerá-las no design. Você só projeta aquilo que o ambiente de desenvolvimento não cria para você.

Projetando Classes de Entidade

Durante a análise, as classes de entidade representam unidades manipuladas de informações. Muitas vezes, são passivas e persistentes e podem ser identificadas e associadas ao mecanismo de análise para persistência. Os detalhes do design de um mecanismo de persistência baseado em banco de dados são abordados em Tarefa: Design de Banco de Dados. Considerações de desempenho podem forçar alguma recriação das classes persistentes, provocando alterações no Modelo de Design que são discutidas em conjunto entre a Função: Designer de Banco de Dados e a Função: Designer.

Uma discussão mais ampla dos problemas de design das classes persistentes será apresentada mais tarde sob o título Identificar Classes Persistentes.

Projetando Classes de Controle

Um objeto de controle é responsável pelo gerenciamento do fluxo de um caso de uso e, portanto, coordena a maioria de suas ações; objetos de controle encapsulam a lógica que não está particularmente relacionada às questões da interface com o usuário (objetos de limite) ou da engenharia de dados (objetos de entidade). Essa lógica às vezes é chamada de lógica do aplicativo ou lógica de negócios.

Leve em consideração as seguintes questões quando classes de controle forem projetadas:

  • Complexidade - Você pode manipular comportamentos descomplicados, de coordenação ou de controle, utilizando classes de limite ou de entidade. Entretanto, conforme aumenta a complexidade do aplicativo, podem surgir obstáculos significativos para essa abordagem, tais como:
  • o comportamento de coordenação de casos de uso incorpora-se na UI, tornando mais difícil alterar o sistema
  • a mesma UI não pode ser utilizada em diferentes realizações de casos de uso sem dificuldades
  • a UI fica sobrecarregada com funcionalidade adicional, reduzindo seu desempenho
  • os objetos de entidade ficam sobrecarregados com o comportamento específico do caso de uso, reduzindo sua generalidade

Para evitar esses problemas, as classes de controle foram introduzidas para fornecer o comportamento relacionado à coordenação dos fluxos de eventos.

  • Probabilidade de alteração - Se houver pouca probabilidade de alterar os fluxos de eventos ou o custo for insignificante, a despesa extra e a complexidade de classes de controle adicionais podem não ser justificáveis.
  • Distribuição e desempenho - A necessidade de executar partes do aplicativo em nós diferentes ou em espaços de processo diferentes, leva à necessidade de especializar os elementos de modelo de design. Freqüentemente, essa especialização é acompanhada da inclusão de objetos de controle e da distribuição de comportamento das classes de fronteira e das classes de entidade para as classes de controle. Fazendo isso, as classes de limite passam a fornecer apenas serviços da UI, as classes de entidade passam a fornecer apenas serviços de dados e as classes de controle fornecem o restante.
  • Gerenciamento de transações - Gerenciar transações é uma atividade de coordenação clássica. Sem um estrutura para tratar o gerenciamento de transações, uma ou mais classes de gerenciador de transações teria de interagir para assegurar a manutenção da integridade das transações.

Nos dois últimos casos, se a classe de controle representar um encadeamento de controle separado, será mais apropriado utilizar uma classe ativa para modelar o encadeamento de controle. Em um sistema de tempo real, o uso do Produto de Trabalho: Cápsulas é a abordagem de modelagem preferencial.

Identificar Classes Persistentes

As classes que precisam armazenar seu estado em uma mídia permanente são denominadas persistentes. A necessidade de armazenar seu estado pode ser para registrar permanentemente as informações de classe, para fins de backup em caso de falha do sistema ou para troca de informações. Uma classe persistente pode ter instâncias persistentes ou provisórias; rotular uma classe como persistente significa apenas que algumas instâncias da classe talvez precisem ser persistentes.

Incorpore os mecanismos de design que correspondem aos mecanismos de persistência encontrados durante a análise. Por exemplo, dependendo do que for exigido pela classe, o mecanismo de análise no que diz respeito a persistência pode ser executado por um dos seguintes mecanismos de design:

  • Armazenamento em memória
  • Placa Flash
  • Arquivo binário
  • Sistema de Gerenciamento de Banco de Dados (DBMS)

Os objetos persistentes podem não ser derivados das classes de entidade somente; eles podem também ser necessários para tratar requisitos não-funcionais em geral. Os exemplos seriam os objetos persistentes necessários para manter informações relevantes ao controle de processos ou para manter informações de estado entre as transações.

A identificação de classes persistentes é apropriada para notificar a Função: Designer de Banco de Dados de que a classe exige atenção especial às suas características de armazenamento físico. Ela também notifica a Função: Arquiteto de Software de que a classe precisa ser persistente e a Função: Designerresponsável pelo mecanismo de persistência de que as instâncias da classe precisam se tornar persistentes.

Devido à necessidade de uma estratégia de persistência coordenada, a Função: Designer de Banco de Dados é responsável pelo mapeamento das classes persistentes no banco de dados, utilizando uma estrutura de persistência. Se o projeto estiver desenvolvendo um framework de persistência, o desenvolvedor do framework também será responsável por conhecer os requisitos de persistência das classes de design. Para fornecer a essas pessoas as informações que elas precisam, é suficiente nesse ponto indicar que a classe é persistente ou, mais precisamente, que as instâncias da classe são persistentes.

Definir Visibilidade da Classe

Para cada classe, determine a visibilidade que ela terá no pacote em que reside. Uma classe pública pode ser referenciada fora do pacote que a contém. Uma classe privada (ou uma cuja visibilidade é implementação) só poderá ser referenciada pelas classes que estão dentro do mesmo pacote.

Definir Operações

Identificando Operações

Para identificar operações em classes de design:

  • Estude as responsabilidades de cada classe de análise correspondente criando uma operação para cada responsabilidade. Use a descrição da responsabilidade como a descrição inicial da operação.
  • Estude as realizações de casos de uso nas participações de classe para ver como as operações são utilizadas pelas realizações de casos de uso. Amplie as operações, uma realização de caso de uso por vez, refinando as operações, suas descrições, tipos de retorno e parâmetros. Os requisitos de cada realização de caso de uso pertencentes às classes são descritos textualmente no Fluxo de Eventos da realização de caso de uso.
  • Analise o caso de uso Requisitos Especiais para certificar-se de não faltar nenhum requisito implícito na operação que poderá ser declarada.

As operações devem oferecer suporte às mensagens em diagramas de seqüência porque os scripts - especificações de mensagem temporária que ainda não foram designadas para as operações - descrevem o comportamento esperado da classe. A figura 1 ilustra o exemplo de um diagrama de seqüência.

Diagrama descrito no texto associado.

Figura 1: Mensagens Formam a Base para Identificar Operações

As realizações de casos de uso fornecem informações suficientes para identificar todas as operações. Para encontrar as outras operações, considere o seguinte:

  • Há alguma maneira de inicializar uma nova instância da classe, inclusive por meio da conexão com instâncias de outras classes às quais ela esteja associada?
  • Há necessidade de teste para saber se duas instâncias da classe são iguais?
  • Há necessidade de criar uma cópia de uma instância da classe?
  • Há necessidade de os mecanismos usados pela classe executarem alguma operação? Por exemplo, um mecanismo de coleta de lixo poderia exigir que um objeto elimine todas as referências aos demais objetos para que os recursos não utilizados possam ser liberados.

Não defina operações que apenas obtêm e definem os valores de atributos públicos (consulte Definir Atributos e Definir Associações). Normalmente, essas operações são geradas pelos recursos de geração de código e não precisam ser definidas explicitamente.

Nomeando e Descrevendo as Operações

Utilize as convenções de nomenclatura da linguagem de implementação quando estiver nomeando operações, tipos de retorno, bem como parâmetros e seus tipos. Estes são descritos nas Diretrizes Específicas do Projeto.

Para cada operação, você deve definir:

  • O nome da operação - mantenha-o curto e descritivo do resultado que a operação alcança.
    • Os nomes de operação devem seguir a sintaxe da linguagem de implementação. Exemplo: find_location seria aceitável para C++ ou Visual Basic, mas não para Smalltalk (que não utiliza caractere de sublinhado); um nome mais adequado para todas as linguagens seria findLocation.
    • Evite nomes que sugira como a operação é executada. Por exemplo, Employee.wages() é melhor que Employee.calculateWages(), visto que o último sugere a execução de um cálculo. A operação poderá simplesmente retornar um valor em um banco de dados.
    • O nome da operação deve demonstrar claramente sua finalidade. Evite nomes não específicos, como getData, que não descrevem o resultado que retornam. Use um nome que mostre exatamente o que se espera, como getAddress. Melhor ainda, basta que o nome da operação seja o nome da propriedade que será retornada ou definida. Se ela tiver um parâmetro, ela definirá a propriedade. Se ela não tiver nenhum parâmetro, ela obterá a propriedade. Exemplo: a operação address retorna o endereço de um Cliente, enquanto address(aString) define ou altera o endereço do Cliente. A natureza get e set da operação é implícita à assinatura da operação.
    • As operações conceitualmente idênticas devem ter o mesmo nome, ainda que sejam definidas por classes distintas, implementadas de maneiras totalmente diferentes ou não tenham número igual de parâmetros. Uma operação que cria um objeto, por exemplo, deve ter o mesmo nome em todas as classes.
    • Se as operações em várias classes tiverem a mesma assinatura, a operação deverá retornar o mesmo tipo de resultado, apropriado ao objeto receptor. Esse é um exemplo do conceito de polimorfismo, o qual afirma que objetos diferentes devem responder à mesma mensagem de maneira semelhante. Exemplo: a operação name deve retornar o nome do objeto, não importando como o nome é armazenado ou derivado. Siga esse princípio para facilitar a compreensão do modelo.
  • O tipo de retorno - O tipo de retorno deve ser a classe do objeto retornado pela operação.
  • Uma descrição curta - Tão expressivo quanto possível, o nome da operação muitas vezes é vago e, portanto, não muito útil para tentar entender o que a operação executa. Forneça à operação uma descrição curta que consista em algumas sentenças, escritas segundo a perspectiva do usuário da operação.
  • Os parâmetros - Para cada parâmetro, crie um nome descritivo curto, decida sua classe e forneça uma descrição resumida. Quando especificar parâmetros, lembre-se de que quanto menos parâmetros, maior é a usabilidade. Um número pequeno de parâmetros torna a operação mais fácil se ser entendida, havendo maior probabilidade de encontrar operações semelhantes. Talvez seja necessário dividir uma operação com vários parâmetros em várias operações. A operação deve ser compreendida por aqueles que desejam utilizá-la. A descrição curta deve incluir:
    • o significado dos parâmetros, caso seus nomes não o evidencie
    • se o parâmetro é transmitido por valor ou por referência
    • parâmetros que devem ter valores fornecidos
    • parâmetros que podem ser opcionais e seus valores padrão, se nenhum valor for fornecido
    • intervalos válidos para parâmetros, se aplicável
    • o que é feito na operação
    • quais parâmetros por referência são alterados pela operação

Uma vez definidas as operações, preencha nos diagramas de seqüência as informações sobre quais operações são chamadas para cada mensagem.

Consulte a seção intitulada Diretriz do Produto de Trabalho: Classe de Design, para obter informações adicionais.

Definindo a Visibilidade da Operação

Para cada operação, identifique a visibilidade de exportação da operação dentre estas opções:

  • Pública - a operação é visível para modelar elementos que não sejam a própria classe.
  • Implementação - a operação é visível somente dentro da 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

Escolha a visibilidade que, embora seja a mais restrita, ainda possa atender aos objetivos da operação. Para fazer isso, examine os diagramas de seqüência e, para cada mensagem, determine se ela provém de uma classe fora do pacote do receptor (requer visibilidade pública), de dentro do pacote (requer visibilidade de implementação), de uma subclasse (requer visibilidade protegida) ou da própria classe ou de um amigo (requer visibilidade privada).

Definindo Operações de Classe

Em geral, operações são operações de instância , ou seja, são executadas em instâncias da classe. Em alguns casos, no entanto, uma operação se aplica a todas as instâncias da classe; por isso, é uma operação de escopo de classe. O receptor da operação de classe é de fato uma instância de uma metaclasse - a descrição da própria classe - e não qualquer instância específica da classe. Exemplos de operações de classe incluem mensagens que criam (instanciam) novas instâncias que retornam todas as Instâncias de uma classe.

A cadeia da operação é sublinhada para indicar uma operação de escopo de classe.

Definir Métodos

Um método especifica a implementação de uma operação. Em muitos casos nos quais o comportamento exigido pela operação é suficientemente definido pelo nome, pela descrição e pelos parâmetros da operação, os métodos são implementados diretamente na linguagem de programação. Quando a implementação de uma operação exige o uso de um algoritmo específico ou mais informações do que as apresentadas na descrição da operação, uma descrição de método separada é necessária. O método descreve como a operação funciona, não apenas o que ela faz.

O método deve discutir como fazer o seguinte:

  • as operações serão implementadas
  • os atributos serão implementados e utilizados para implementar operações
  • as relações serão implementadas e utilizadas para implementar operações

Os requisitos irão variar conforme o caso; contudo, as especificações de método para uma classe deverão declarar sempre:

  • o que será feito de acordo com os requisitos
  • quais os outros objetos e suas descrições serão utilizados

Requisitos mais específicos poderão incluir:

  • como os parâmetros serão implementados
  • quais, se houver, algoritmos especiais serão utilizados

Os diagramas de seqüência são uma importante fonte para esse tipo de informação. A partir daí, ficará claro quais operações serão utilizadas em outros objetos quando uma operação for executada. Para a implementação completa de uma operação, é necessária uma especificação de quais operações serão utilizadas em outros objetos. A produção de uma especificação completa de método, portanto, exige identificar as operações para os objetos envolvidos e inspecionar os diagramas de seqüência correspondentes.

Definir Estados

Em algumas operações, o comportamento da operação depende do estado no qual se encontra o objeto receptor. Uma máquina de estado é uma ferramenta que descreve os estados que um objeto pode assumir e os eventos que fazem com que ele se mova de um estado para outro (consulte Técnica: Diagrama de Estados). As máquinas de estado são bastante úteis para descrever as classes ativas. O uso de máquinas de estado é particularmente importante para definir o comportamento do Produto e Trabalho: Cápsulas.

Na figura 2, é mostrado o exemplo de uma máquina de estado simples.

Diagrama descrito no texto associado.

Figura 2: Um Diagrama de Estado Simples de uma Bomba de Combustível

Cada evento de transição de estado pode ser associado a uma operação. Dependendo do estado do objeto, a operação poderá ter um comportamento diferente e os eventos de transição descrevem como isso ocorre.

A descrição do método referente à operação associada deve ser atualizada com informações específicas do estado, indicando a cada estado relevante o que a operação deve fazer. Os estados muitas vezes são representados utilizando atributos; os diagramas de estado servem como entrada na etapa de identificação dos atributos.

Para obter informações adicionais, consulte Técnica: Diagrama de Estados.

Definir Atributos

Durante a definição de métodos e a identificação de estados, são identificados os atributos que a classe precisa para executar suas operações. Os atributos fornecem armazenamento de informações para a instância da classe e muitas vezes são utilizados para representar o estado da instância da classe. Qualquer informação que a própria classe mantém ela o faz através de seus atributos. Para cada atributo, defina:

  • seu nome, que deve obedecer às convenções de nomenclatura da linguagem de implementação e do projeto
  • seu tipo, que será um tipo de dado elementar suportado pela linguagem de implementação
  • seu valor padrão ou inicial, com o qual é inicializado quando novas instâncias da classe são criadas
  • a visibilidade, que irá adotar um dos seguintes valores:
    • Público: o atributo é visível dentro e fora da 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 da linguagem)
    • Privado: o atributo é visível somente para a própria classe e para amigos da classe
    • Implementação: o atributo é visível somente para a própria classe
  • classes persistentes, se o atributo é persistente (o padrão) ou temporário. Embora a própria classe possa ser persistente, nem todos os atributos da classe precisam ser persistentes

Verifique se todos os atributos são necessários. Os atributos devem ser justificados - é fácil incluir os atributos no início do processo e mantê-los após não mais serem necessários, por falta de perspectiva. Atributos extras, multiplicados por milhares ou milhões de instâncias, podem causar um efeito prejudicial sobre os requisitos de desempenho e armazenamento de um sistema.

Consulte a seção intitulada Atributos em Diretriz de Produto de Trabalho: Classe de Design, para obter informações adicionais sobre os atributos.

Definir Dependências

Para cada caso em que a comunicação entre os objetos é necessária, levante as seguintes questões:

  • A referência a um receptor é transmitida como um parâmetro para a operação? Se a resposta for sim, estabeleça uma dependência entre as classes emissora e receptora de um diagrama que contenha as duas classes. Além disso, se o formato do diagrama de comunicação para interações for utilizado, qualifique a visibilidade de link e defina-a como parâmetro.
  • O receptor é global? Se a resposta for sim, estabeleça uma dependência entre as classes emissora e receptora de um diagrama que contenha as duas classes. Além disso, se o formato do diagrama de comunicação para interações for utilizado, qualifique a visibilidade de link e defina-a como global.
  • O receptor é um objeto temporário criado e destruído durante a própria operação? Se a resposta for sim, estabeleça uma dependência entre as classes emissora e receptora de um diagrama que contenha as duas classes. Além disso, se o formato do diagrama de comunicação para interações for utilizado, qualifique a visibilidade de link e defina-a como local.

Observe que os links modelados dessa forma são temporários, existindo apenas por um período limitado no contexto específico da colaboração - nesse sentido, eles são instâncias da função de associação na colaboração. Contudo, a relação em um modelo de classe (isto é, independente de contexto) deve ser de dependência, conforme descrito anteriormente. Conforme [RUM98] declara, na definição de link temporário: "É possível modelar todos esses links como associações, mas as condições nas associações devem ser declaradas de forma bem ampla e eles perdem muito de sua precisão quando limitam combinações de objetos." Nesta situação, a modelagem de uma dependência é menos importante do que a modelagem do relacionamento na colaboração, porque dependência não descreve o relacionamento completamente; somente que ele existe.

Definir Associações

As associações proporcionam o mecanismo para os objetos se comunicarem entre si. Elas fornecem aos objetos um canal pelo qual as mensagens podem fluir. Além disso, documentam as dependências entre as classes, destacando que as mudanças em uma classe podem ser percebidas entre muitas outras classes.

Examine as descrições de método para cada operação a fim de entender como as instâncias da classe se comunicam e colaboram com outros objetos. Para enviar uma mensagem para outro objeto, um objeto deve ter uma referência para o receptor da mensagem. Um diagrama de comunicação (representação alternativa de um diagrama de seqüência) mostrará a comunicação do objeto em termos de links, conforme ilustrado na figura 3.

Diagrama descrito no texto associado.

Figura 3: Exemplo de um Diagrama de Comunicação

Definindo Associações e Agregações

As mensagens restantes utilizam associação ou agregação para especificar a relação entre instâncias de duas classes que se comunicam. ConsulteTécnica: Associação e Técnica: Agregação, para obter informações sobre a escolha da representação apropriada. No caso dessas duas associações, defina a visibilidade de link como campo nos diagramas de comunicação. Outros tarefas:

  • Estabelecer a navegabilidade das associações e agregações. Para fazer isso, considere que tipo de navegabilidade é necessária nas respectivas instâncias de link, nos diagramas de interação. Visto que a navegabilidade, por padrão, é verdadeira, você só precisa encontrar as associações (e agregações) nas quais todas as funções de links opostos de todos os objetos de uma classe na associação não exigem navegabilidade. Nesses casos, defina a navegabilidade como falsa na função da classe.
  • Se houver atributos na própria associação (representada por classes de associação), crie uma classe de design para representar a classe de associação, com os atributos adequados. Intercale essa classe entre as outras duas classes e estabeleça associações com a multiplicidade adequada entre a classe de associação e as outras duas classes.
  • Especifique se as extremidades da associação devem ou não ser ordenadas; é o ocorre quando os objetos associados a um objeto na outra extremidade da associação tem uma ordenação que deve ser preservada.
  • Se a classe associada (ou agregada) for consultada apenas pela classe atual, considere a possibilidade de aninhar a classe. As vantagens do aninhamento de classes incluem serviço de mensagens mais rápido e um modelo de design mais simples. As desvantagens incluem espaço para a classe aninhada alocado estaticamente, mesmo que haja instâncias da classe aninhada, falta de identidade do objeto separada da classe de inclusão ou incapacidade para consultar instâncias de classes aninhadas fora da classe de inclusão.

Associações e agregações são melhor definidas em um diagrama que representa as classes associadas. O diagrama de classes deve ser de propriedade do pacote que contém as classes associadas. A figura 4 ilustra um exemplo de diagrama de classe, representando associações e agregações.

Diagrama descrito no texto associado.

Figura 4: Exemplo de um Diagrama de Classe Mostrando Associações, Agregações e Generalizações entre as Classes

Manipulando Associações de Assinatura entre Classes de Análise

As associações de assinatura entre as classes de análise são utilizadas para identificar dependências de eventos entre as classes. No Modelo de Design, é necessário tratar essas dependências de eventos explicitamente, seja utilizando as estruturas de rotina de tratamento de eventos disponíveis, seja projetando e criando sua própria estrutura de rotina de tratamento de eventos. Em algumas linguagens de programação, como Visual Basic, isso é feito diretamente , ou seja, você declara, promove e trata os eventos correspondentes. Em outras linguagens, talvez seja preciso utilizar uma biblioteca adicional de funções reutilizáveis para tratar assinaturas e eventos. Se não for possível adquirir a funcionalidade, ela terá de ser projetada e criada. Consulte também Técnica: Associação de Assinatura.

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

Quando encontrar uma classe complexa, com estrutura complexa, crie um diagrama de estrutura composta para essa classe. Modele as partes que executarão as funções referentes ao comportamento da classe. Estabeleça como as partes serão 'ligadas' com o uso de conectores. Utilize portas com interfaces declaradas, se deseja permitir que clientes diferentes dessa classe acessem partes específicas do comportamento oferecido por essa classe. Utilize também portas que isolem completamente de seu ambiente as partes internas dessa classe.

Para obter informações adicionais sobre este tópico e exemplos sobre o diagrama de estrutura composta, consulte Conceito: Classe Estruturada.

Definir Generalizações

As classes poderão ser organizadas em uma hierarquia de generalização para refletir um comportamento e uma estrutura comuns. Uma superclasse comum pode ser definida para que suas subclasses possam herdar tanto o comportamento quanto a estrutura. A generalização é uma conveniência notacional que permite definir estrutura e comportamento comuns em um só local e reutilizá-los onde for encontrado comportamento e estrutura repetidos. Consulte Técnica: Generalização, para obter informações adicionais sobre relacionamentos de generalização.

Ao encontrar uma generalização, crie uma superclasse comum para conter atributos, associações, agregações e operações comuns. Remova o comportamento comum das classes que passarão a ser subclasses da superclasse comum. Defina um relacionamento de generalização da subclasse para a superclasse.

Resolver Conflitos entre Casos de Uso

A finalidade desta etapa é evitar conflitos simultâneos causados quando há probabilidade de dois ou mais casos de uso acessarem ao mesmo tempo as instâncias da classe de design, de maneira possivelmente inconsistente.

Uma das dificuldades de utilizar um caso de uso após o outro no processo de design é o fato de que dois ou mais casos de uso podem tentar chamar operações simultaneamente nos objetos de design, de maneiras possivelmente conflitantes. Nesses casos, os conflitos de simultaneidade devem ser identificados e resolvidos explicitamente.

Se o serviço de mensagens síncronas for utilizado, a execução de uma operação bloqueará as chamadas subseqüentes, até a que a operação seja concluída. Serviço de mensagens síncronas implica uma ordem para o processamento das mensagens na qual o primeiro a chegar é o primeiro a ser atendido. Isso poderá resolver o conflito de simultaneidade, principalmente nos casos em que todas as mensagens têm a mesma prioridade ou quando cada mensagem é executada no mesmo encadeamento. Nos casos em que um objeto pode ser acessado por diversos encadeamentos de execução (representados por classes ativas), devem ser usados mecanismos explícitos para impedir ou resolver o conflito de simultaneidade.

Em sistemas de tempo real, nos quais os encadeamentos são representados pelo Produto de Trabalho: Cápsulas, esse problema ainda aguarda uma solução para o acesso simultâneo múltiplo a objetos passivos, enquanto as próprias cápsulas fornecem um mecanismo de enfileiramento e impõem a semântica de execução até a conclusão para manipular o acesso simultâneo. A solução recomendada é encapsular os objetos passivos, evitando assim o problema do acesso simultâneo através da semântica da própria cápsula.

É possível que diversas operações executadas no mesmo objeto sejam chamadas simultaneamente por encadeamentos de execução distintos sem ocorrer conflito de simultaneidade; o nome e o endereço de um cliente podem ser modificados ao mesmo tempo sem que haja conflito. O conflito só ocorre quando dois encadeamentos de execução diferentes tentam modificar a mesma propriedade do objeto.

Para cada objeto que pode ser acessado simultaneamente por diversos encadeamentos de execução, identifique as seções de código que precisam ser protegidas contra acesso simultâneo. No início da fase de Elaboração, será impossível identificar segmentos de código específicos; as operações que devem ser protegidas serão suficientes. Em seguida, selecione ou projete os mecanismos de controle de acesso apropriados para evitar o conflito devido ao acesso simultâneo. Exemplos desses mecanismos incluem o enfileiramento de mensagens para serializar o acesso, o uso de semáforos ou símbolos para permitir o acesso a apenas um encadeamento por vez ou outras variantes de mecanismos de bloqueio. A escolha do mecanismo tende a ser altamente dependente de implementação e normalmente varia conforme a linguagem de programação e o ambiente operacional. Consulte as Diretrizes Específicas do Projeto, para orientação ao selecionar mecanismos de simultaneidade.

Manipular Requisitos Não Funcionais em Geral

As Classes de Design são refinadas para manipular requisitos gerais, não funcionais, Um dado inicial importante para essa etapa são os requisitos não funcionais de uma classe de análise, que podem já estar declarados em seus requisitos e responsabilidades especiais. Tais requisitos muitas vezes são especificados em termos dos mecanismos arquiteturais (de análise) necessários para realizar a classe; nessa etapa, a classe é refinada para incorporar os mecanismos de design correspondentes a esses mecanismos de análise.

Os mecanismos de design disponíveis são identificados e caracterizados pelo arquiteto de softwarePara cada mecanismo de design necessário, qualifique quantas características puder, informando grupos quando possível. Consulte Tarefa: Identificar Mecanismos de Design, Conceito: Mecanismos de Análise e Conceito: Mecanismos de Design e de Implementação, para obter informações adicionais sobre mecanismos de design.

Pode haver diversos mecanismos e diretrizes gerais de design que precisam ser considerados ao projetar classes, tais como:

  • utilizar produtos e componentes existentes
  • adaptar-se à linguagem de programação
  • distribuir objetos
  • alcançar um desempenho aceitável
  • alcançar níveis de segurança específicos
  • tratar erros
Avaliar Seus Resultados

Verifique o modelo de design neste estágio para saber se seu trabalho está na direção certa. Não há necessidade de revisar o modelo em detalhe, mas considere as seguintes listas de verificação:



Informações Adicionais