Projeto de objetos em Sistemas

Quando identificamos os conceitos em nosso sistema, identificamos o que ele deve ser capaz de realizar, isto é, identificamos as suas responsabilidades. Podemos esperar que os conceitos dependam da ajuda de outros conceitos, isto é, que eles colaborem entre si para realizar as suas responsabilidades.” (SERAFINI, 2008, p. 95)

Responsabilidade e colaboração 

Veja o caso de uso “Pedir Emprestado um Livro” em um sistema de Biblioteca. O modelo de domínio (ou conceitual) para o sistema de Biblioteca obtido está representado na Figura 8.1 a seguir.

* responsabilidade é aquilo que um objeto deve ser capaz de fazer/executar. Esse conceito diz respeito às próprias ações que um objeto deve fazer, ou seja, são suas próprias responsabilidades dentro do sistema.

*colaboração é aquilo que um objeto pode fazer/executar para outro objeto, de modo que o caso de uso possa ser realizado. Dessa forma, um objeto ajuda o outro a executar aquilo que é a responsabilidade do outro.

Podemos identificar a colaboração de vários “objetos”: 

a) o Usuário pede a colaboração do Bibliotecário; 
b) o Bibliotecário pede a colaboração do Catálogo; 
c) o Bibliotecário pede a colaboração do Assistente. 

Assim temos:

Definição de padrão

Padrão, de uma forma geral, é tudo aquilo que serve de solução genérica para problemas semelhantes. Os especialistas em qualquer profissão adotam procedimentos padrões para a solução de problemas que ocorrem comumente em suas tarefas.

*padrão É uma solução muito utilizada e genérica para um problema.

Vamos examinar algumas definições:

Os padrões são sempre escritos (ou identificados) obedecendo às seguintes informações: nome do padrão, problema que ele se propõe resolver, solução apresentada pelo padrão.

Os padrões GRASP

Segundo Serafini (2008), Craig Larman adaptou os critérios fundamentais de projeto ao formato dos padrões de projeto e foi muito feliz com essa abordagem. 

A utilização dos padrões de projeto propostos por Larman (2007) pode melhorar bastante nossos diagramas de colaboração e, por conseguinte, nossos projetos. 

Larman deu nome a seu conjunto de regras de GRASP que significa “General Responsibility Assigment Software Patterns,“ ou seja, Padrões de “Software“ Genéricos de Atribuição de Responsabilidades, e esses padrões podem nos ajudar a alocar responsabilidades (ou comportamentos) às classes de uma forma muito prática. 

Os padrões GRASP são:

a) “Expert“ – Especialista
b) “Creator“ – Criador
c) “High cohesion“ – Alta coesão
d) “Low coupling“ – Baixo acoplamento
e) “Controller“ – Controlador

Os nomes de padrões geralmente não são traduzidos para o português.

Grasp 1: “Expert“

É um padrão muito simples e um dos mais desobedecidos.

O padrão “Expert“ responde à questão:

Dado um comportamento, a que classe deve ser atribuído esse comportamento?

Resposta: À classe que contém a informação desejada. A classe especialista (“Expert“) na informação!

A resposta a essa pergunta leva à alocação inteligente dos comportamentos, que por sua vez resulta em sistemas mais fáceis de entender, de se expandir, de se reutilizar e ainda mais robustos.

Veja o exemplo a seguir:

Considere três classes: uma representando Pedidos, outra LinhaPedido e outra CatalogoDeProduto. Um trecho do modelo conceitual é (Figura 8.2 ):


Agora suponha que estamos construindo a colaboração para um dos casos de uso e esse caso de uso necessita que o total de um pedido seja apresentado ao usuário.

A que classe deve ser alocado o comportamento calcularTotal()?

O padrão “Expert“ diz que a única classe que deve ser permitida lidar com o Custo Total de um pedido é a classe Pedido – isso porque ela deve ser especialista sobre todas as “coisas” relativas a pedidos.

Então alocamos o método calcularTotal() à classe Pedidos.

Mas para calcular o total do pedido, este precisa saber o total de cada linha de Pedido.

A opção de deixar que Pedido acessasse as informações de LinhaPedido e realizasse a soma tornaria mais pobre nosso projeto.

Mais uma vez aplicamos o padrão “Expert“, e temos que a única classe que deveria calcular o total de Linha de Pedido é LinhaPedido; assim, criamos um método calcularSubTotal() e alocamos em LinhaPedido.

Por sua vez, calcularSubTotal() precisa obter o preço unitário do produto; então, o “Expert“ sugere alocar o método obterPreco() a CatalogoProduto. 

Assim teremos o diagrama de classes parcial mostrado na Figura 8.3.


E nosso diagrama de colaboração está mostrado na Figura 8.4.


Síntese:

Nome: Expert

Problema: Dado um comportamento, a que classe deve ser atribuído esse comportamento?

Solução: À classe que contém a informação desejada. A classe especialista (“expert“) na informação (LARMAN, 2007).

Grasp 2: “Creator“

O padrão “Creator“ é uma especialização do padrão “Expert“.

Ele responde à questão:

Quem deve ser responsável pela criação de instâncias de uma classe particular?

A resposta é que a classe A deve ser responsável pela criação dos objetos da classe B se:

a) A contém objetos B; 
b) A utiliza objetos B; 
c) A contém os dados de inicialização que devem ser passados à classe B.

No nosso exemplo anterior, quando um novo pedido é criado, quem deve ser responsável pela criação dos objetos LinhaPedido?

A solução apontada pelo padrão “Creator“ é que, como pedido contém LinhaPedido (veja a Figura 8.5), então a classe Pedido (e somente ela) deve ser responsável pela criação de Linhas de Pedidos.


O diagrama de colaboração está mostrado na Figura 8.6:


Síntese:

Nome: Creator

Problema: Quem deve ser responsável pela criação de instâncias de uma classe particular?

Solução: a classe A deve ser responsável pela criação dos objetos da classe B se:

a) A contém objetos B;
b) A utiliza objetos B; 
c) A contém os dados de inicialização que devem ser passados a classe B.

(LARMAN, 2007)

Grasp 3: “High cohesion“

Uma alta coesão significa que as responsabilidades de uma classe são fortemente relacionadas. Uma classe que faz muitas coisas não relacionadas possui uma baixa coesão.

É extremamente importante garantir que as responsabilidades de cada classe sejam focalizadas. Em um bom projeto orientado a objetos, cada classe não deve realizar trabalho excessivo. Um sinal de que o projeto orientado a objetos é bom é cada classe possuir um pequeno número de métodos.

Exemplo 

No projeto de um sistema de controle de elevadores, uma classe ControladorElevadores foi projetada (Figura 8.7): 


Observe como a classe ControladorElevadores realiza várias funcionalidades de trabalho: liga alarmes, inicia, desativa, move o elevador... 

Esse não é um bom projeto, porque a classe não é coesiva, ela está tentando realizar muitas coisas diferentes. 

Essa classe deve ser difícil de manter, e não está claro o que ela deve fazer, isto é, quais as suas responsabilidades. 

Nosso sistema de controle de elevador está tentando modelar no mínimo umas três abstrações: um alarme, as portas do elevador e o registro de falhas... 

Separando essas abstrações, teríamos um projeto melhor para o sistema de controle de elevadores. Veja a Figura 8.8:



Síntese:

Nome: “High Cohesion”

Problema: Como manter a coesão alta?

Solução: Cada classe deve representar uma única “coisa” (ou abstração) do mundo real.

(LARMAN, 2007)

 Grasp 4: “Low Coupling”

Acoplamento é uma medida de quão dependente uma classe é de outra.

Um acoplamento alto entre módulos leva a um projeto que é difícil de manter ou modificar – uma simples mudança em uma classe pode ocasionar mudanças em várias outras classes (SERAFINI, 2008).

O diagrama de colaboração fornece um excelente meio de verificar o nível de acoplamento do projeto. O diagrama de classes também dá uma ideia do nível de acoplamento. Veja o diagrama mostrado na Figura 8.9.



Todas essas associações são necessárias?

O projetista desse sistema deveria questionar seriamente alguns detalhes de projeto.

Exemplo:

– Por que a classe C está associada à classe A, se existe uma associação indireta pela classe B? Essa associação pode ter sido criada para melhorar o desempenho, o que seria razoável, ou pode ter sido criada por desleixo...

– Por que a classe B possui tantas associações? Essa classe provavelmente está realizando muito trabalho.

Seguir o modelo conceitual é uma forma excelente de reduzir o acoplamento. Somente envie mensagem de uma classe à outra se a associação foi identificada durante a fase de modelagem conceitual. Agindo assim, você estará se restringindo a introduzir acoplamentos que existem no mundo real.

Exemplos:

No nosso sistema de Pedidos, identificamos no modelo conceitual que Clientes possuem Pedidos.

No caso de uso “Criar Pedidos”, que classe deve ser responsável por criar um novo pedido?

O padrão “Creator“ sugere que a classe Cliente deva ser responsável (Figura 8.10):


Assim, acoplamos Cliente a Pedidos. Neste caso, está “OK“, pois eles são acoplados no mundo real (veja no modelo conceitual). 

A seguir, uma vez que Pedido foi criado, o caso de uso precisa adicionar as linhas de pedidos a Pedidos. Quem deve ser responsável por adicionar as linhas de pedidos? 

Uma solução seria Cliente adicionar as linhas (afinal ele possui as informações de inicialização necessárias: quantas linhas, que produtos, que quantidades, etc.). Essa solução é mostrada na Figura 8.11:


 

Essa solução aumentou o acoplamento artificialmente; temos agora todas as três classes dependentes umas das outras. 

Se fizermos Pedido responsável pela criação das linhas de pedidos, faremos com que Cliente não tenha nenhum acoplamento com LinhaPedidos (Figura 8.12):


Se a implementação da classe LinhaPedido sofrer alguma mudança, a única classe afetada vai ser a classe Pedido. 

Todo acoplamento que existe nesse projeto foi identificado no modelo conceitual:

a) Clientes possuem Pedidos;
b) Pedidos possuem LinhaPedidos.

E faz muito sentido a existência desses acoplamentos.

Síntese:

Nome: “LOW COUPLING“

Problema: Como manter o acoplamento baixo?

Solução: Faça uma análise e procure reduzir o acoplamento ao mínimo (LARMAN, 2007)

Grasp 5: “Controller”

Vamos examinar um Sistema de Apostas de Jogos e o caso de uso Fazer Apostas.

Para que o usuário possa fazer as apostas, precisamos que o sistema permita entrar com as apostas e mostre os resultados dos jogos.

Como o usuário vai entrar com as apostas e como os resultados serão mostrados?

Vamos examinar a responsabilidade de mostrar os resultados de uma partida para o usuário: Que classe deve ser responsável por satisfazer esse requisito?

A aplicação do padrão “Expert“, sugere que, como os detalhes pertencem a Partidas, então a classe Partida deve ser a “Expert“ em mostrar os detalhes relevantes (Figura 8.13):


Essa solução pode parecer boa, mas na realidade ela viola o padrão “Expert“!

A classe Partida deve ser especialista em todas as informações sobre as Partidas, e não ter a responsabilidade de mostrar os resultados das partidas na Interface Gráfica (GUI).

Geralmente, a inclusão de informações sobre a GUI, Bases de Dados, (ou qualquer outro objeto físico) em nossas classes resultará em um projeto pobre.

Imagine se tivéssemos 100 classes em nosso sistema e muitas dessas classes devessem ler e escrever na tela do computador. O que aconteceria se os requisitos do sistema mudassem e fosse necessário alterar toda a interface com o usuário? Teríamos que alterar cada classe envolvida com a interface com o usuário.

Fica muito melhor manter todas as classes do modelo conceitual “puras” – chamamos essas classes de Classes de Negócio” ou “Business Classes”.

O padrão “Controller“ é uma solução possível. Criamos uma nova classe que vai ficar entre o usuário e as classes de negócio. Essa classe funciona como uma ponte (ou controlador) entre a Interface Gráfica e as Classes do Domínio. 

O nome dessa classe geralmente é:

<nomeDoCasoDeUSo>Handler.

Então, em nosso projeto, precisamos criar uma classe FazerApostasHandler (Figura 8.14):


No caso de termos de alterar a interface com o usuário, a única classe que precisamos modificar é a classe controladora.

Síntese:

Nome: “Controller”

Problema: Como manter a GUI separada das classes de negócio?

Solução: Crie uma classe controladora para o sistema ou caso de uso.

(LARMAN, 2007)

Resumo

Com o estudo dos padrões apresentados nesta aula, aprendemos a definir responsabilidades a objetos dentro de nossos projetos que garantirão a melhoria constante de nossos “softwares“. Os padrões apresentam soluções bem-sucedidas para vários problemas que comumente os desenvolvedores encontram no momento de definirem seus objetos e sua interação com os demais. 

Os padrões propostos por Larman (2007) são conhecidos pelos desenvolvedores de todo o mundo e representam no meio profissional uma excelente solução para situações que enfrentamos no trabalho diário de projetar sistemas.

Além desses padrões, outros também são conhecidos no mercado profissional: são os padrões do GoF (““Group of Four“”, na língua portuguesa, “Grupo dos Quatro”). São quatro autores de outros padrões que atendem também a outras classes de problemas de projeto, apresentando soluções muito interessantes para a construção de “software“ de qualidade e efetividade. Seus padrões são apresentados em Gamma et al. (2000). 


Postagens mais visitadas deste blog

O modelo em cascata: um modelo sequencial

Representação dos Elementos do Diagrama de classes

instalar WordPress no computador (localhost)