Revisando o Design
O design é melhor realizado colaborativamente, porque se trata de uma atividade de solução de problemas usando uma gama de partes e perspectivas. Deve existir um nível constante de revisão para garantir que as decisões façam sentido para a área a ser projetada e no design global do sistema. Existem também algumas ocasiões quando alguma área de design é revisada por um grupo de pessoas interessadas ou de conhecimento, tais como o arquiteto que irá verificar se o design está em conformidade com uma decisão arquitetural, ou um desenvolvedor que poderá implementar o design.
O design deve ser examinado para assegurar que ele segue heurísticas de design de qualidade, tais como baixo acoplamento e alta coesão. As responsabilidades devem ser adequadamente distribuídas pelos elementos de forma que não existam alguns elementos com muita responsabilidade e outros sem responsabilidade nenhuma. O design deve ser capaz de comunicar claramente as decisões de design, mesmo sem aprofundar em preocupações que serão mais bem tratadas durante a implementação do código.
Assegure-se que o design obedeça a qualquer diretriz específica do projeto e esteja em conformidade com a arquitetura. As modificações no design para melhorá-lo (com base em questões identificadas nas revisões) devem aplicar a Re-fatoração para assegurar que o design e qualquer implementação do design existente continuem cumprindo as suas responsabilidades. Revise os relacionamentos entre os elementos para melhorar o acoplamento no design. Remova relacionamentos redundantes, tente fazer com que os relacionamentos sejam unidirecionais, e assim por diante. Veja Guideline: Analisar o Design para mais informações.Aperfeiçoar o Design
Após a criação de uma implementação que inclui um conjunto de elementos que colaboram entre si, com o comportamento e os relacionamentos robustos o bastante para passar nos testes de desenvolvedor, o design pode ser melhorado e transformado em um sistema mais robusto e de fácil manutenção.
A visibilidade de cada operação deve ser selecionada de forma a ser o mais restritiva possível. Ao percorrer um cenário, deve ficar claro quais operações devem estar disponíveis para outros elementos do design e quais podem ser consideradas um comportamento dentro do elemento que tem a operação. A minimização da quantidade de operações públicas cria um design mais compreensível e fácil de manter.
Em relação aos parâmetros, valor de retorno e a descrição de como elas executam o comportamento, as operações podem ser detalhadas em um nível mais baixo que direcione a implementação real, ou o detalhe pode ser deixado para ser tratado quando da escrita do código.
Atributos de dados podem ser identificados com base nas informações necessárias para suportar o comportamento ou em requisitos adicionais, tais como informações a serem apresentadas ao usuário ou transmitidas para outro sistema. Evite a análise de domínio indiscriminada, porque pode existir uma grande quantidade de dados no domínio que não seja necessária para suportar os requisitos. Os atributos de dados podem simplesmente ser identificados ou podem ser projetados de forma detalhada com tipos de atributo, valores iniciais e restrições. Decida sobre a visibilidade do atributo de dados; operações para acessar e atualizar os dados podem ser adicionadas ou adiadas até a implementação.
A generalização e as interfaces podem ser aplicadas para simplificar ou melhorar o design. Assegure que o uso destas técnicas realmente melhore o design, ao invés de somente aumentar a sua complexidade. Por exemplo, o comportamento comum pode ser fatorado em uma classe pai através da generalização ou para fora em uma classe auxiliar através de delegação. Esta última solução pode ser mais compreensível e de fácil manutenção, porque a generalização é um relacionamento inflexível (veja a seguir a seção sobre herança).
O refinamento de qualquer parte do design pode incluir uma verificação no processo de design. Você pode descobrir que o que estava inicialmente identificado como um único comportamento de um elemento merece uma análise detalhada dos elementos colaboradores que realizam o comportamento.
Ao atualizar um design existente - especialmente aquele que já teve trechos implementados - aplique a Re-fatoração para garantir que o design melhorado continua a desempenhar como esperado.
Organize os elementos
Em um design de qualquer tamanho notável, os elementos devem ser organizados em pacotes. Atribua os elementos a pacotes existentes ou novos, e garanta que os relacionamentos de visibilidade entre os pacotes suportem a navegação necessária entre os elementos. Decida para cada elemento de ele deve ser visível para os elementos que estão fora do pacote.
Quando da estruturação do design em pacotes, considere a Dividir em camadas e outros padrões. Embora todos os trabalhos de design devam estar em conformidade com as decisões arquiteturais existentes, a alocação de elementos em pacotes e possíveis alterações da visibilidade do pacote são preocupações arquiteturais significantes. O desenvolvedor deve colaborar com o arquiteto para garantir que as decisões a nível de pacote, estejam em conformidade com o resto da arquitetura.
Esta diretriz cita primeiro a identificação e design dos elementos e, em seguida, a organização dos elementos em pacotes. Entretanto, esta não é uma ordem estrita de eventos. Não há nada de errado com a identificação de uma estrutura de pacotes para o sistema e, em seguida, o preenchimento da estrutura com os elementos identificados, visto que é permitido que os verdadeiros elementos identificados influenciem na estrutura de pacotes resultante. Veja as seções sobre a identificação e o comportamento dos elementos em Guideline: Analisar o Design.
Identifique os padrões
A identificação de Padrão e a busca por oportunidades para alavancar os padrões são técnicas úteis. O valor dos padrões aqui é que eles fornecem um atalho para um design robusto. Por exemplo, quando há uma interface realizada por várias classes, é possível que um padrão Abstract Factory seja de grande utilidade, porque este padrão encapsula a lógica de qual classe deve ser instanciada. Quão mais experiente for um desenvolvedor, melhor ele será na identificação de oportunidades para aproveitar, ou alavancar os padrões.
Quanto mais você usar os padrões, mais fácil será identificar oportunidades para alavanca-los. A princípio, procure por lugares onde você possa especificar claramente a necessidade de algum comportamento. Talvez exista um lugar onde alguma função ou algoritmo deva ser compartilhada entre muitas classes diferentes. Como esse comportamento pode ser compartilhado entre classes heterogêneas? Ou talvez uma biblioteca de terceiros substituirá um bloco de código personalizado. Existe uma forma de tornar essa transição mais fácil pela criação de uma interface que possa usar ambas as implementações? Existem possibilidades para encontrar ou eventualmente criar um padrão.
Veja também [GAM95] e [SHA05]
Herança de comportamento versus Herança de interfaces
A herança (ou generalização) é frequentemente usada como um atalho durante a implementação para reutilizar rapidamente o comportamento (código).
Cuidado: Trabalhe arduamente para remover a herança de comportamento no design. Ela quase sempre despende mais esforços do que economiza.
A herança é uma estrutura muito rígida com regras restritas. Uma classe que herda de outra classe estabelece um relacionamento do ripo é-uma. A classe Herdada é um tipo de classe pai - a classe filho tem os mesmos relacionamentos e comportamentos da classe pai. Na maioria das hierarquias, será impossível manter este tipo de relacionamento. As exceções crescem rapidamente, e é comum encontrar classes filho que removem ou sobrepõem o comportamento da classe pai. Isto aumenta os custos de manutenção e torna difícil o entendimento do que cada classe faz.
Também é muito tentador instanciar classes pai, o que faz com que elas sejam tanto abstratas quanto concretas. Se uma classe tem filhos, ela deve ser suficientemente abstrata para suportar o comportamento generalizado dos filhos. Mas se ela for instanciada, ela deverá ser concreta o suficiente para fornecer o comportamento específico. Raramente é possível atender estes imperativos concorrentes ao mesmo tempo, fazendo com que o design sofra.
Use relacionamentos de associação e agregação, ao invés do comportamento de herança. Os padrões são uma boa ferramenta para alavancar a eliminação das hierarquias de herança.
Herdar as interfaces é mais seguro, pois só a descrição e não a implementação do que é necessário ser feito será reutilizada.
Evitar o comportamento de herança é uma aplicação do "Open-Closed Principle". Veja Concept: Design para mais informações.
Revise a análise
A Guideline: Analisar o Design descreve técnicas que também são úteis para a obtenção de um design mais robusto.
Considere a arquitetura
A arquitetura deve ser considerada em todas as mudanças no design. O "melhor" design para uma determinada parte da solução pode não ser adequado por causa de restrições arquiteturais que devem suportar o sistema como um todo. A arquitetura também pode ajudar a tomar decisões de design, porque ela pode fazer parte dos critérios de seleção entre duas ou mais soluções possíveis. Os desenvolvedores devem sempre estar atualizados com a arquitetura e revisá-la frequentemente, especialmente nas iterações iniciais.
Esta diretriz comenta sobre a conformidade com a arquitetura de várias formas; foi escrita considerando o design com uma arquitetura pré-existente. Embora os projetos geralmente tenham uma arquitetura pré-existente disponível, uma arquitetura é o resultado das atividades de design. Pois, além de debater a conformidade com alguma arquitetura existente, você também deve considerar a criação da arquitetura, bem como atualizações e melhorias com base no trabalho de design.
Veja também [SHA05] para uma útil introdução as técnicas orientadas a objeto que poderão ser aplicadas quando da evolução de um bom design. |