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:
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).













