Atividade:
|
Finalidade
|
|
Função: Designer | |
Freqüência: Uma vez por iteração. | |
Etapas
|
|
Artefatos de Entrada: | Artefatos Resultantes: |
Mentores de Ferramentas: | |
More Information: |
Detalhes de Workflow: |
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.
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.
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 atividade (incluindo novas classes, operações, atributos e relações), mas em conformidade 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, não apenas como a primeira etapa desta atividade. Além disso, eles são freqüentemente aplicados a um conjunto de classes, e não apenas a uma única classe.
Crie uma ou várias classes de design inicial para a classe de análise determinada como entrada para esta atividade 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.
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ê.
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 Atividade: Design de Banco de Dados. Considerações de desempenho podem forçar uma nova decomposição em fatores das classes persistentes, provocando mudanças 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 das questões de design das classes persistentes será apresentada mais tarde no título Identificar Classes Persistentes.
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 aplicativa ou lógica de negócios.
Leve em consideração as seguintes questões quando classes de controle forem projetadas:
- o comportamento de coordenação de casos de uso incorpora-se na UI, tornando mais difícil fazer mudanças no 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.
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.
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:
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. É apropriada também para notificar a Função: Arquiteto de Software de que a classe precisa ser persistente e a Função: Designer responsá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.
Para cada classe, determine a visibilidade que ela terá no pacote em que reside. Uma classe pública pode ser consultada fora do pacote que a contém. Uma classe privada (ou uma cuja visibilidade é implementação) só poderá ser consultada pelas classes que estão dentro do mesmo pacote.
Para identificar operações em classes de design:
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.
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:
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.
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.
Para cada operação, você deve definir:
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 com o título Operações de Classe em Diretrizes: Classes de Design para obter informações adicionais.
Para cada operação, identifique a visibilidade de exportação da operação dentre estas opções:
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).
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 e assim por diante.
A cadeia da operação é sublinhada para indicar uma operação de escopo de classe.
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.
Se descrito, o método deve abordar como:
Os requisitos irão variar conforme o caso; contudo, as especificações de método para uma classe deverão declarar sempre:
Requisitos mais específicos poderão incluir:
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.
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 mude de um estado para outro (consulte Diretrizes: Diagrama de Estados). As máquinas de estado são bastante úteis para descrever as classes ativas.
Na figura 2, é mostrado o exemplo de uma máquina de estado simples.
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 Diretrizes: Diagrama de Estados.
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:
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 com o título Atributos em Diretrizes: Classe de Design para obter informações adicionais.
Para cada caso em que a comunicação entre os objetos é necessária, levante as seguintes questões:
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] explica, 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; além disso, eles perdem muito de sua precisão quando limitam combinações de objetos." Nesse caso, a modelagem de uma dependência é menos importante que a modelagem da relação na colaboração, pois a dependência não descreve a relação completamente, apenas que ela existe.
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.
Figura 3: Exemplo de um Diagrama de Comunicação
As mensagens restantes utilizam associação ou agregação para especificar a relação entre instâncias de duas classes que se comunicam. Consulte Diretrizes: Associação e Diretrizes: Agregação para obter informações sobre como escolher a representação apropriada. No caso dessas duas associações, defina a visibilidade de link como campo nos diagramas de comunicação. Outros tarefas:
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.
Figura 4: Exemplo de um Diagrama de Classe Mostrando Associações, Agregações e Generalizações entre as Classes
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 Diretrizes: Associação de Assinatura.
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.
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 composto, consulte Conceitos: Classe Estruturada.
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 Diretrizes: Generalização para obter informações adicionais sobre relações 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 uma relação de generalização da subclasse para a superclasse.
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.
É 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.
As Classes de Design são refinadas para tratar 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 software. Para cada mecanismo de design necessário, qualifique quantas características puder, informando grupos quando possível. Para obter informações adicionais sobre mecanismos de design, consulte Atividade: Identificar Mecanismos de Design, Conceitos: Mecanismos de Análise e Conceitos: Mecanismos de Design e Implementação.
Pode haver diversos mecanismos e diretrizes gerais de design que precisam ser considerados ao projetar classes, tais como:
Verifique o modelo de design neste estágio para saber se seu trabalho está na direção certa. Não há necessidade de rever o modelo detalhadamente, mas leve em conta os seguintes pontos de verificação:
Rational Unified Process
|