Segue uma lista de artigos sobre Princípios GRASP, sempre bom estudar isto!

A maioria dos programadores aprendeu que em POO devemos mapear objetos reais para objetos em seu modelo de classes. Isto muitas vezes é seguido a risca, mas nem sempre é a melhor maneira de modelar suas classes, pois muitos detalhes cruciais só são visíveis ao se considerar o contexto geral, ou seja, a forma como esses objetos vão interagir e como eles vão colaborar uns com os outros. Os padrões GRASP surgiram exatamente para auxiliar nesta questão.

 

O GRASP descreve ao todo nove padrões ou princípios, a saber:

 

  1. Especialista de informação (Expert)
  2. Criador (Creator)
  3. Alta coesão (High coesion)
  4. Baixo acoplamento (Low coupling)
  5. Controlador (Controller)
  6. Polimorfismo (Polymorphism)
  7. Invenção pura (Pure Fabrication)
  8. Indireção (Indirection)
  9. Variação protegida (Protected Variations)

 

Cada um destes padrões trata de um problema específico, e sugere uma solução de arquitetura para superá-lo. A solução pode utilizar alguns dos consagrados padrões GoF (que poderá ser assunto aqui no AgileZ), portanto é recomendável que o programador ou analista conheça bem soluções como Observer pattern, Proxy, Iterator, Adapter, Strategy, Singleton entre outros.

 

Antes de começar a descrever cada um dos nove padrões, convém conceituar o que é responsabilidade. Uma responsabilidade pode ser descrita como uma obrigação, seja obrigação de fazer ou conhecer alguma coisa.

 

Fazer algo:

 

  • a si mesmo
  • começar ações em outros objetos
  • controlar ações em outros objetos

 

Conhecer algo:

 

  • informações encapsuladas
  • objetos relacionados
  • informações que pode calcular

 

Exemplo: uma classe Memo pode ter obrigação de gerar linhas (faz algo a si mesma), ou pode utilizar uma classe Linha para isso (inicia uma ação em outro objeto) e controlar essas ações mantendo um contador de linhas (informações encapsuladas). Para isso, o Memo deve conhecer a classe Linha (objetos relacionados).

 

Não devemos, entretanto, confundir métodos/operações com responsabilidades. Um ou vários métodos devem ser utilizados para implementar uma determinada responsabilidade.

 

I. O padrão Expert (Especialista de informação)

 

O primeiro padrão que deve ser considerado para resolver o problema de atribuição de responsabilidade é o Expert, que diz que o primeiro candidato a receber a responsabilidade é aquele que possui a informação necessária para executar a tarefa.

 

O exemplo mais comum é o da venda. Nosso sistema precisa calcular o total de uma venda. Qual classe deve ser responsável por fazer este cálculo?

 

vendas

 

Sabemos que, para calcular o total de uma venda, precisamos somar os totais de cada item, além do desconto e juros, se houver. Olhando o diagrama, fica fácil observar que a classe Venda é a classe que mantém a lista de itens (dona da informação), portanto ela é a primeira candidata a calcular o total.

 

venda 2

 

Utilizando o padrão Expert, atribuímos à classe Venda a responsabilidade por calcular o total da venda. Como será feito o cálculo internamente? A classe Venda deve iterar pelos itens, somando os valores de cada um e depois subtrair os descontos e somar os acréscimos. Mas, e o valor do item, quem deve calcula-lo? Aplicando novamente o padrão Expert, podemos observar que é o Item quem possui a informação necessária para essa tarefa (quantidade), e o produto é quem possui o preço, logo, as responsabilidades, seguindo o padrão Expert, ficariam assim:

 

venda 3

 

É lógico que este foi um exemplo bem simples, e provavelmente nenhum analista precisaria conhecer o GRASP para chegar ao modelo proposto, mas, ao tratar de relacionamentos e projetos mais complexos, fica bem mais difícil manter um modelo consistente, altamente escalável e flexível, sem conhecer bem esses padrões.

 

 

Criador

Problema – Quem cria um objeto X?

Solução – Atribua ao objeto Y a tarefa de criar X quando:
1 – Y “contém” X (Ex: Carro contém Roda, Carro pode criar as Rodas)
2 – Y registra X (Ex: Agenda registra DadosPessoais, Agenda pode criar DadosPessoais)
3 – Y usa X (Ex: Sistema usa Painel, Sistema pode criar Painel)

Criador ruim.
Criador bom.

 

Especialista na Informação
Problema – Que objeto deve ficar com a responsabilidade X?

Solução – Atribua a responsabilidade X ao objeto que tenha as informações para cumprir a responsabilidade.

Ex: Dado um sistema de compras em que é preciso resgatar o valor de uma compra, qual objeto deve retornar o valor da compra?

Provavelmente nesse sistema deverá existir um objeto como NotaFiscal ou Venda que possui todos os itens da compra, então é responsabilidade de algum desses objetos retornar o valor da compra.

Especialista ruim.
Especialista bom.

Acoplamento Baixo
Problema – Como reduzir o impacto de modificações de regras de negócios?

Solução – Atribua responsabilidades aos objetos para que o acoplamento permaneça baixo.

Uma diretriz para deixar o acoplamento baixo é utilizar o padrão/princípio Especialista na Informação.

Péssimo acoplamento.

Controlador
Problema – Quando um evento é lançado da GUI, que objeto deve receber e controlar esses eventos?

Solução – Atribuir essa responsabilidade a um objeto que represente o sistema ou um caso de uso (controlador de sessão de caso de uso).

O padrão/princípio Controlador apóia o Acoplamento Baixo, pois evita da GUI estar diretamente conectada com objetos do domínio da aplicação.

Péssimo controlador.
Bom controlador.

Coesão Alta
Problema – Como manter os objetos gerenciáveis e focados, apoiando o Acoplamento Baixo?

Solução – Atribuir responsabilidades de modo que a Coesão permaneça alta. Uma diretriz para essa solução é pensar em objetos especialistas em suas ações.

Coesão ruim.

Polimorfismo
Problema – Como criar componentes de software interconectáveis?

Solução – Use operações polimórficas para atribuir responsabilidades aos tipos que tem comportamento variado.

Polimorfismo.

Invenção Pura
Problema – A qual objeto atribuir uma responsabilidade quando o princípio Especialista não fornece soluções apropriadas?

Solução – Crie uma classe artificial que não represente um conceito de domínio e que apóie coesão alta, acoplamento baixo e reuso.

Um exemplo de invenção pura são objetos para persistência de dados. Não representam um conceito de domínio e o padrão especialista fornece soluções não coesas (colocar métodos de persistência dentro da classe do objeto, por exemplo).

Indireção
Problema – A que objeto deve-se atribuir uma responsabilidade a fim de evitar acoplamento?

Solução – Atribuir a responsabilidade a um objeto mediador, um intermediário.

Variações Protegidas
Problema – Como evitar que as variações não afetem outros elementos do sistema?

Solução – Identificar possíveis pontos em que possam ocorrer alguma mudança e criar uma interface estável em torno deles.

Esses nove padrões/princípios fornecem diretrizes básicas para um bom projeto de objetos guiando o projetista pelo caminho que proporciona melhor robustez ao software.

Mais:

Ótimo artigo com exemplos:

http://www.dsc.ufcg.edu.br/~jacques/cursos/map/html/pat/expert.htm

Artigo na devmedia:

http://www.devmedia.com.br/websys.4/webreader.asp?cat=6&revista=javamagazine_98#a-4155

 


More:

 

Patterns from the book “Applying UML and Patterns – An Introduction to Object-Oriented Analysis and Design and Iterative Development” authored by Craig Larman.

  1. Information Expert
  2. Creator
  3. Controller
  4. Low Coupling
  5. High Cohesion
  6. Polymorphism
  7. Pure Fabrication
  8. Indirection
  9. Protected Variations

Purpose

Describe fundamental principles of object design and responsibility assignment.

Information Expert

The purpose of this pattern is to provide a solution to the problem of determining which object should undertake a particular responsibility. Idea is to assign chosen responsibility to the information expert – the class that has all the information necessary to fulfill it.

For example, if we have two classes Product and ProductSaver then Product may be an information expert on answering its price and ProductSaver, in his turn, on saving Product to the database.

Creator

Provides information about which object should be responsible for creating new instances of particular class(es). Idea is to assign class B the responsibility to create an instance of class A if one or more of the following is true:

  • B aggregates A objects.
  • B is connected to the instance(s) of class A via composition.
  • B records instances of A objects.
  • B is closely associated with A objects.
  • B is an information expert with respect to creating A (thus, B has information enough to create an instance of class A)

Controller

Gives answer to the question “Who should be responsible for handling an input system event?”. Idea is to assign the responsibility to a class representing one of the following choices:

  • Represents the overall system, device, or subsystem (facade controller).
  • Represents a use-case scenario within which the system event occurs. The name of such controller class often ends with “Handler” or “Session” (use-case or session controller).

Note: Controller classes should delegate the work to the other objects and not to fulfill the task by themselves. Normally, they just control the flow of activity.

Low Coupling

Determines how to assign responsibilities to achieve:

  • low dependency between classes
  • low change impact
  • high reuse potential

Idea is to assign responsibilities to the classes so that coupling remains low as possible.

High Cohesion

Provides answer to the question “How to keep complexity manageable?”. Idea is to assign a responsibility so that cohesion remains high. That is, eliminate classes (functions, systems, etc.) which do too much work or just many unrelated things.

Polymorphism

Idea is to assign responsibility of defining the variation of behaviors based on type to the types for which this variation happens.

Pure Fabrication

A pure fabrication provides ability to add operations that do not conceptually belong to any domain object. As a matter of fact, it is a class that takes on responsibilities from some domain class(es) that would be assigned those responsibilities based on the Information Expert pattern. The whole intend is to gain low coupling, high cohesion and reuse, which in case of using Information Expert pattern would be doubtful.

Indirection

Supports low coupling (and reuse potential) between two classes by assigning the responsibility of mediation interactions to an intermediate object. For example, this can be done using Adapter design pattern.

Protected Variations

Addresses the problem of assigning responsibilities in such a way that the variations and (or) instability that might occur is some elements does not have undesirable impact on other parts of the designed system. The idea is to:

  1. Identify points of predicted variation or instability.
  2. Assign responsibilities to create a stable interface around them.

This principle has two additional variations, which are best explained in the original way:

  • Information Hiding

We propose instead that one begins with a list of difficult design decisions or design decisions which are likely to change. Each module is then designed to hide such a decision from the others.“, Parnas.

  • Open-Closed Principle

Modules should be both open (for extension; adaptable) and closed (the module is closed to modification in ways that affect clients).“, Bertrand Meyer.

 

 

Fontes: http://www.gabrielamorim.com.br/projetando-objetos-usando-os-principios-grasp

http://javaobsession.wordpress.com/grasp/