Você está na página 1de 55

Padrões de Projeto

LIVRO 02
Padrões GRASP
Sumário

1. Padrões 4. Intermediário 5. Avançado


Porque usar High cohesion Polymorphism
Como funciona Low coupling Pure fabrication
Onde surgiu o conceito Indirection Protected variations
Como se cria um padrão
6. Referências
2. Padrões GRASP
3. Básico
Creator
Information expert
Controller
Padrões
Um Padrão de Projeto descreve uma solução comprovada para um problema recorrente e conhecido
no desenvolvimento de software orientado a objetos.

1 2 3 4 5 6 7 8 9 10
Padrões Porque usar

O objetivo dos padrões é codificar conhecimento existente de uma forma que possa ser
reaplicado em contextos diferentes.
Os pesquisadores da ciência da computação reconhecem a importância dos Padrões de Projeto
porque são soluções elegantes, simples e reutilizáveis.
Os principais benefícios para a utilização dos padrões de projeto:
• Fornecem soluções que já foram testadas e aprovadas.
• Tornam o sistema mais fácil de entender e manter.
• Facilitam o desenvolvimento de módulos coesos.
• A comunicação entre os participantes
do projeto fica mais eficiente.

1 2 3 4 5 6 7 8 9 10
Padrões Como funciona

Quando estamos codificando, encontramos muitos problemas que se repetem, e as soluções que
costumamos encontrar acabam se repetindo também.
Documentar um padrão é uma maneira de:
reusar a solução sempre que o problema reaparece;
compartilhar a solução que você aprendeu com outros programadores.
Esta solução se torna um padrão quando é reconhecida como a melhor maneira de se resolver
um problema específico de codificação de software.

1 2 3 4 5 6 7 8 9 10
Padrões Onde surgiu o conceito

A origem dos Design Patterns vem do trabalho de um arquiteto chamado


Christopher Alexander, no final da década de 70.
Ele escreveu dois livros: “A Pattern Language” e “A Timeless Way of Building”,
nos quais ele exemplificava o uso e descrevia seu raciocínio para
documentar os padrões.
Neles ele estabelece que um padrão deve ter as características:
• Encapsulamento: capturam o conhecimento e a experiência de especialistas.
• Generalidade: definem um vocabulário comum para a discussão de problemas e soluções de
projeto, permitindo o uso por toda a comunidade.
• Equilíbrio: uma solução não basta ser recorrente, mas precisa também ser uma boa solução.
• Abstração: especificam abstrações que estão acima do nível de classes ou objetos isolados
ou de componentes.
• Combinatoriedade: deve haver um relacionamento com os outros padrões, para mostrar outros
que oferecem uma outra alternativa, ou padrões que podem complementar essa solução para
compensar suas desvantagens.

1 2 3 4 5 6 7 8 9 10
Padrões Como se cria um padrão

Embora um padrão seja a descrição de um problema, de uma solução genérica e sua justificativa,
isso não significa que qualquer solução conhecida para um problema possa constituir um padrão.
Existem características obrigatórias que devem ser atendidas pelos padrões:
• Devem possuir um nome, que descreva o problema, as soluções e consequências. Um nome
permite definir o vocabulário a ser utilizado pelos projetistas e desenvolvedores em um nível mais
alto de abstração.
• Todo padrão deve relatar de maneira clara a qual(is) problema(s) ele deve ser aplicado, ou
seja, quais são os problemas que quando inserido em um determinado contexto o padrão
conseguirá resolvê­lo. Alguns podendo exigir pré­condições.
• Solução descreve os elementos que compõem o projeto, seus relacionamentos, responsabilidades
e colaborações. Um padrão deve ser uma solução concreta, ele deve ser exprimido em forma de
gabarito (algoritmo) que, no entanto, pode ser aplicado de maneiras diferentes.
• Todo padrão deve relatar quais são as suas consequências para que possa ser analisada a
solução alternativa de projetos e para a compreensão dos benefícios da aplicação do projeto.

1 2 3 4 5 6 7 8 9 10
Padrões GRASP
Os padrões GRASP (abreviatura do inglês “General Responsibility
Assignment Software Patterns”) são formados por princípios/padrões
que servem de base para a atribuição de responsabilidades em
um projeto orientado a objetos.
Estes patterns foram publicados originalmente pelo especialista
Craig Larman no livro “Applying UML and Patterns – An Introduction
to Object­Oriented Analysis and Design and the Unified Process”
(obra traduzida em português com o título “Utilizando UML e
Padrões – Uma introdução à análise e ao projeto orientados a
objetos e ao desenvolvimento iterativo”).

1 2 3 4 5 6 7 8 9 10
Padrões GRASP
O conceito de responsabilidade deve ser compreendido, basicamente, como as obrigações
que um objeto possui quando se leva em conta o seu papel dentro de um determinado
contexto.
Para se definirem as responsabilidades de um objeto, leva­se em conta o que este elemento irá
“fazer” e/ou “saber”. Além disso, é preciso considerar ainda as prováveis colaborações
(interações) entre diferentes objetos.
Os diferentes padrões GRASP não devem ser encarados como soluções pré­definidas para
problemas específicos. Na verdade, estes patterns devem ser compreendidos como princípios
que auxiliam os desenvolvedores na árdua tarefa de projetar de uma forma bem estrutura
aplicações orientadas a objetos.

1 2 3 4 5 6 7 8 9 10
Criador (Creator) Caso de Uso e
Diagrama Conceitual

Diagrama Conceitual
Caso de Uso
creator
Quem deve ser responsável por criar uma nova
instância de uma classe?
Pedido PedidoItem
Nas seguintes situações, a classe B (criador) terá a lista de itens produto
responsabilidade de criar uma instância da classe Contém quantidade

A (criada): criarNovoItem

­ criador B contém ou agrega a criada A


Contém
­ criador B registra a existência da criada A
­ criador B usa a criada A Produto
­ criador B tem os dados necessários para a nome
inicialização da criada A que serao passados ao preço

construtor da criada A.

1 2 3 4 5 6 7 8 9 10
Criador (Creator) Implementação
Passo 1: implemente a classe Produto Passo 2: implemente a classe PedidoItem
Esta classe vai ser o cadastro de produtos da loja. Esta classe vai ser o item do pedido, cada pedido
poderá ter vários destes itens.

package creatorexemplo; package creatorexemplo;


public class Produto { public class PedidoItem {
private String nome; private Produto produto;
private double precoUnitario; private int quantidade;
public Produto(String nome, double precoUnitario){ public PedidoItem(Produto produto, int quantidade){
this.nome = nome; this.produto = produto;
this.precoUnitario = precoUnitario; this.quantidade = quantidade;
} }
public String getNome() { return nome; } public Produto getProduto() { return produto; }
public double getPrecoUnitario() { return precoUnitario; } public int getQuantidade() { return quantidade; }
@Override @Override
public String toString() { public String toString() {
return "Nome do Produto: "+this.nome+"\nPreço Unitário: "+this.precoUnitario; return "-----------Dados do item--------- \n"+this.produto+"\nQuantidade: "
} +this.quantidade+"\n---------------------------------\n";
} }
}

1 2 3 4 5 6 7 8 9 10
Criador (Creator) Implementação
Passo 3: implemente a classe Pedido
package creatorexemplo;
Esta classe vai ser o cadastro de pedidos da loja. import java.util.ArrayList;
import java.util.List;
Esta é a classe Creator, já que é ela que controlará public class Pedido {
a criação de outra classe: a classe PedidoItem. private List<PedidoItem> itens = new ArrayList<>();
Ela segue a regra: "criador B contém a criada A", já public void criarNovoItem(Produto produto, int quantidade){
PedidoItem pedidoItem = new PedidoItem(produto,quantidade);
que o pedido contém os itens do pedido. this.itens.add(pedidoItem);
}
public List<PedidoItem> getItens() {
return itens;
}
public void mostrarItensPedido(){
for(PedidoItem item : itens){
System.out.println(item);
}
}
}

1 2 3 4 5 6 7 8 9 10
Criador (Creator) Implementação
Passo 4: crie a classe TesteCreator
Mas antes, veja abaixo, como seria o código se não tivessemos seguido o padrão Creator.
O pedido existem sem que tenha item criado para ele, só no final do código o item é assossiado ao pedido.
Já usando o padrão, usamos um método da classe pedido para criar os itens, isso faz muita diferença porque
encapsula a responsabilidade de criar os itens do pedido para o próprio pedido.

Pedido pedido = new Pedido(); package creatorexemplo;


Produto produto = new Produto(); public class TesteCreator {
produto.setNome("Fatia de Pizza");
produto.setPreco(3.50); public static void main(String[] args) {
Produto produto1 = new Produto("Batata",0.3);
PedidoItem pedidoItem = new PedidoItem(produto); Produto produto2 = new Produto("Cenoura",0.25);
pedido.getItens().add(pedidoItem); Pedido pedido1 = new Pedido();
//O pedido que tem a tarefa de inicializar o item e adicioná-lo
pedido1 .criarNovoItem(produto1 , 1 5);
pedido1 .criarNovoItem(produto2, 1 2);
pedido1 .mostrarItensPedido();
}
}

1 2 3 4 5 6 7 8 9 10
Especialista (Information Expert) Caso de Uso

Caso de Uso
Conhecido como Especialista na Informação, ou apenas Especialista, é um padrão de projeto que possui uma
abordagem genérica que visa atribuir a responsabilidade de fazer ou conhecer algo ao "especialista na
informação" — a classe que possui a informação necessária para cumprir tal responsabilidade.

É o padrão que atribui responsabilidades a quem realmente detêm a informação necessária para preencher os
requisitos e suprir aquela responsabilidade.

É o padrão mais usado de todos. A informação necessária para uma determinada classe geralmente está espalhada
entre várias classes, e através deste padrão é possível descobrir aonde esta informação deve ficar.

As consequências do uso deste padrão é que o encapsulamento é mantido, já que objetos usam sua própria
informação para cumprir responsabilidades, leva ao fraco acoplamento entre objetos e à alta coesão já que objetos
fazem tudo que é relacionado à sua própria informação.

1 2 3 4 5 6 7 8 9 10
Especialista (Information Expert) Diagrama Conceitual

locadora

Fita Aluguel Cliente


Contém titulo Contém fita Contém nome
Tipo tipodefita diasAlugada coleçãodeAlugueis

1 2 3 4 5 6 7 8 9 10
Especialista (Information Expert) Implementação
Passo 1: implemente o conjunto Tipo Passo 2: implemente a classe Fita
Implementar o enum faz com que as informações a
package expert;
respeito dos tipos de fita possíveis fique totalmente
encapsulado neste elemento. public class Fita {
//Atributos
private String titulo;
Enum é um conjunto feito em Java. private Tipo tipodefita;
//Construtor
public Fita(String titulo, Tipo codigodepreco) {
package expert; this.titulo = titulo;
this.tipodefita = codigodepreco;
public enum Tipo { normal, lancamento, infantil } }
public String getTitulo() { return titulo; }
public Tipo getTipodefita() { return tipodefita; }
public void setTipodefita(Tipo tipodefita) { this.tipodefita = tipodefita; }
}

1 2 3 4 5 6 7 8 9 10
Especialista (Information Expert) Implementação
package expert;
Passo 3: implemente a classe Aluguel public class Aluguel {
private Fita fita;
private int diasAlugada;
Veja que aqui se faz novamente uma referência aos public Aluguel(Fita fita, int diasAlugada) {
tipos de fita, para definir a lógica de negócio this.fita = fita;
this.diasAlugada = diasAlugada;
relativo à cobrança para cada tipo de fita. }
public Fita getFita() { return fita; }
public int getDiasAlugada() { return diasAlugada; }
public double getValorAluguel() {
double valorAluguel = 0.0;
switch (fita.getTipodefita()) {
case normal:
valorAluguel += 2;
if (this.getDiasAlugada() > 2) { valorAluguel += (this.getDiasAlugada() - 2) * 1 .5; }
break;
case lancamento:
valorAluguel += this.getDiasAlugada() * 3;
break;
case infantil:
valorAluguel += 1 .5;
if (this.getDiasAlugada() > 3) { valorAluguel += (this.getDiasAlugada() - 3) * 1 .5; }
break;
}
return valorAluguel;
}
}

1 2 3 4 5 6 7 8 9 10
Especialista (Information Expert) Implementação
Passo 5: crie a classe Cliente

package expert; // determina valores para cada linha


valorCorrente += cada.getValorAluguel();
import java.util.ArrayList;
import java.util.Collection; // trata de pontos de alugador frequente
pontosDeAlugadorFrequente++;
public class Cliente { // adiciona bonus para aluguel de um lançamento por pelo menos 2 dias
private String nome; if (cada.getFita().getTipodefita() == Tipo.lancamento
&& cada.getDiasAlugada() > 1 ) {
//Array de fitas alugadas pontosDeAlugadorFrequente++;
private Collection<Aluguel> fitasAlugadas = new ArrayList<Aluguel>(); }
public Cliente(String nome) { this.nome = nome; } // mostra valores para este aluguel
resultado += "\t" + "R$" + valorCorrente + "\t" + cada.getFita().getTitulo() + "\n" ;
public String getNome() { return nome; }
valorTotal += valorCorrente;
public void adicionaAluguel(Aluguel aluguel) { }
fitasAlugadas.add(aluguel);
} // adiciona rodapé
resultado += "\nValor total devido: " + valorTotal + "\n";
public String extrato() { resultado += "Voce acumulou " + pontosDeAlugadorFrequente
final String fimDeLinha = System.getProperty("line.separator"); + " pontos de alugador frequente";
double valorTotal = 0.0; return resultado;
int pontosDeAlugadorFrequente = 0; }
String resultado = "Registro de Alugueis de " + getNome() + fimDeLinha; }
for (Aluguel f : fitasAlugadas) {
double valorCorrente = 0.0;
Aluguel cada = f;

1 2 3 4 5 6 7 8 9 10
Especialista (Information Expert) Implementação
Passo 6: crie a classe Expert

package expert;
public class Expert {
public static void main(String[] args) {
Fita fita1 = new Fita("Avatar 2", Tipo.lancamento);
Fita fita2 = new Fita("Festa da Salsicha", Tipo.infantil);
Aluguel alug1 = new Aluguel(fita1 , 5);
Aluguel alug2 = new Aluguel(fita2, 2);
Cliente cliente = new Cliente("Daniel");
cliente.adicionaAluguel(alug1 );
cliente.adicionaAluguel(alug2);
System.out.println(cliente.extrato());
}
}

1 2 3 4 5 6 7 8 9 10
Controlador (Controller) Caso de Uso e
Diagrama Conceitual

Caso de Uso
O Controller implementa o MVC, sigla para Model (modelo), View (visão) e Controller (Controle), que facilita a troca
de informações entre a interface do usuário aos dados no banco. Cada uma das camadas executa o que é sua
responsabilidade e nada mais que isso.

A utilização deste padrão isola as regras de negócios da lógica de apresentação (UX) para o usuário. Pode­se
produzir várias interfaces com o usuário que independem das regras de negócios, proporcionando assim muito mais
flexibilidade e oportunidades de reuso das classes.

Diagrama Conceitual
Lógica de Eventos do
negócios usuário

Resultado do Dados para


processamento visualização

1 2 3 4 5 6 7 8 9 10
Controlador (Controller) Conceitos

Essa classe também é conhecida como Business Object Model (objeto modelo de negócio).
Sua responsabilidade é controlar a forma como os dados se comportam por meio das funções,
lógica e regras de negócios estabelecidas.
Ele é o detentor dos dados que recebe as informações do Controller, valida se estão corretos ou
não e envia a resposta mais adequada.

A camada de controle é responsável por intermediar as requisições enviadas pelo View com as
respostas fornecidas pelo Model.
Numa analogia bem simplista, o controller operaria como o ‘’maestro de uma orquestra’’ que
permite a comunicação entre o detentor dos dados e a visão que é apresentada ao usuário.

Essa camada é responsável por apresentar as informações de forma visual ao usuário, onde a
principal preocupação são as mensagens, botões ou telas.
O View está na linha de frente da comunicação com o usuário e é responsável por transmitir
questionamentos ao controller e entregar as respostas obtidas ao usuário.
É a parte da interface que disponibiliza e captura todas as informação do usuário.

1 2 3 4 5 6 7 8 9 10
Controlador (Controller) Implementação

view model controller

ContatoDisplayView <<interface>> ContatoModel


ContatoView nome
display
atualizaContatoView sobrenome
cargo
views empresa Controla
lista de views

createGui calcComissao
calcComissao(x)
apresentação
ContatoEditController
ContatoEditView
todos os elementos
da janela de Controla
edição
createGui

1 2 3 4 5 6 7 8 9 10
Controlador (Controller) Implementação
public class Contato{
private String nome;
Passo 1: implemente a classe Contato private String sobrenome;
private String cargo;
Esta classe não será utilizada, porque ela private String empresa;
private ContatoView view;
possui apenas uma view possível
public Contato(ContatoView v){
nome = ""; sobrenome = ""; cargo = ""; empresa = ""; view = v;
}
public String getNome() { return nome; }
public String getSobrenome(){ return sobrenome; }
public String getCargo() { return cargo; }
public String getEmpresa() { return empresa; }
public void setNome(String newNome) { nome = newNome; }
public void setSobrenome(String newSobrenome) { sobrenome = newSobrenome; }
public void setCargo(String newCargo) { cargo = newCargo; }
public void setEmpresa(String newEmpresa) { empresa = newEmpresa; }
public void updateModel(String newNome, String newSobrenome, String newCargo, String newEmpresa){
if ((newNome != null) && !newNome.equals("")) { setNome(newNome); }
if ((newSobrenome != null) && !newSobrenome.equals("")) { setSobrenome(newSobrenome); }
if ((newCargo != null) && !newCargo.equals("")) { setCargo(newCargo); }
if ((newEmpresa != null) && !newEmpresa.equals("")) { setEmpresa(newEmpresa); }
updateView();
}
private void updateView() { view.atualizaContatoView(nome, sobrenome, cargo, empresa); }
}

1 2 3 4 5 6 7 8 9 10
Controlador (Controller) Implementação
Passo 2: implemente a classe ContatoModel import java.util.ArrayList;
import java.util.Iterator;
Esta classe será utilizada, porque ela servirá para public class ContatoModel {
implementar o componente Model. private String nome;
private String sobrenome;
Este modelo vai ser usado para repassar private String cargo;
private String empresa;
informação para dois componentes do tipo View. private ArrayList<ContatoView> contactViews = new ArrayList<ContatoView>();
public ContatoModel() { this(null); }
public ContatoModel(ContatoView view){
nome = "";
sobrenome = "";
cargo = "";
empresa = "";
if (view != null) {
contactViews.add(view);
}
}
public void addContactView(ContatoView view){
if (!contactViews.contains(view)){
contactViews.add(view);
}
}

1 2 3 4 5 6 7 8 9 10
Controlador (Controller) Implementação
public void addContactView(ContatoView view){ private boolean isEmptyString(String input){
if (!contactViews.contains(view)){ return ((input == null) || input.equals(""));
contactViews.add(view); }
}
} private void updateView(){
Iterator<ContatoView> notifyViews = contactViews.iterator();
public void removeContactView(ContatoView view){ while (notifyViews.hasNext()){
contactViews.remove(view); ((ContatoView)notifyViews.next()).atualizaContatoView(nome, sobrenome, cargo,
} empresa);
}
public String getNome() { return nome; } }
public String getSobrenome() { return sobrenome; } }
public String getCargo() { return cargo; }
public String getEmpresa() { return empresa; }
public void setNome(String newNome) { nome = newNome; }
public void setSobrenome(String newSobrenome) { sobrenome = newSobrenome; }
public void setCargo(String newCargo) { cargo = newCargo; }
public void setEmpresa(String newEmpresa) { empresa = newEmpresa; }
public void updateModel(String newNome, String newSobrenome, String newCargo, String
newEmpresa) {
if (!isEmptyString(newNome)) { setNome(newNome); }
if (!isEmptyString(newSobrenome)) { setSobrenome(newSobrenome); }
if (!isEmptyString(newCargo)) { setCargo(newCargo); }
if (!isEmptyString(newEmpresa)) { setEmpresa(newEmpresa); }
updateView();
}

1 2 3 4 5 6 7 8 9 10
Controlador (Controller) Implementação
Passo 3: implemente a interface ContatoView import java.awt.BorderLayout;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
Esta interface obriga as classes que são view a import javax.swing.JTextArea;
implementarem um método para atualizar os dados public class ContatoDisplayView extends JPanel implements ContatoView{
do contato.
private static final long serialVersionUID = 1 L;
private JTextArea display;
public interface ContatoView{ public ContatoDisplayView() { createGui(); }
public void atualizaContatoView(String nome, String sobrenome, public void createGui(){
String cargo, String empresa); setLayout(new BorderLayout());
} display = new JTextArea(1 0, 40);
display.setEditable(false);
JScrollPane scrollDisplay = new JScrollPane(display);
this.add(scrollDisplay, BorderLayout.CENTER);
Passo 4: implemente a classe ContatoDisplayView }
public void atualizaContatoView(String newNome, String newSobrenome, String newCargo,
A ContactDisplayView vai fornecer uma janela de String newEmpresa){
display que reflete as mudanças feitas na janela de display.setText("CONTATO ATUALIZADO:\nNOVOS VALORES:\n" +
edição. "\tNome: " + newNome + " " + newSobrenome + "\n" +
"\tCargo: " + newCargo + "\n" + "\tEmpresa: " + newEmpresa);
}
Esta janela não suporta interação com o usuário, e }
porisso nao precisa de um controller.

1 2 3 4 5 6 7 8 9 10
Controlador (Controller) Implementação
Passo 5: implemente a classe ContatoEditView import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
A classe ContactEditView vai fornecer uma janela import java.awt.event.ActionListener;
para editar o contato e vai trabalhar com um import javax.swing.BoxLayout;
controller. import javax.swing.JButton;
import javax.swing.JLabel;
Ua observação importante sobre as duas view import javax.swing.JPanel;
import javax.swing.JTextField;
(ContatoDisplayView e ContatoEditView): observe public class ContatoEditView extends JPanel implements ContatoView{
que estas classes são filhas da classe JPanel, que é
private static final long serialVersionUID = 1 L;
uma classe que implementa uma janela do Windows. private static final String BOTAO_ATUALIZAR = "Atualizar";
private static final String BOTAO_SAIR = "Sair";
E elas contrataram a interface ContatoView. private static final String CONTATO_NOME = "Nome ";
private static final String CONTATO_SOBRENOME = "Sobrenome ";
private static final String CONTATO_CARGO = "Cargo ";
private static final String CONTATO_EMPRESA = "Empresa ";
private static final int NOME_COL_WIDTH = 25;
private static final int SOBRENOME_COL_WIDTH = 40;
private static final int CARGO_COL_WIDTH = 25;
private static final int EMPRESA_COL_WIDTH = 40;
private ContatoEditController controller;
private JLabel nomeLabel, sobrenomeLabel, cargoLabel, empresaLabel;
private JTextField nome, sobrenome, cargo, empresa;
private JButton update, exit;

1 2 3 4 5 6 7 8 9 10
Controlador (Controller) Implementação
public ContatoEditView(ContatoModel model){ labelPanel.add(nomeLabel);
controller = new ContatoEditController(model, this); labelPanel.add(sobrenomeLabel);
createGui(); labelPanel.add(cargoLabel);
} labelPanel.add(empresaLabel);
public ContatoEditView(ContatoModel model, ContatoEditController newController){ editPanel.add(labelPanel);
controller = newController;
createGui(); JPanel fieldPanel = new JPanel();
} fieldPanel.setLayout(new GridLayout(0, 1 ));
public void createGui(){ fieldPanel.add(nome);
update = new JButton(BOTAO_ATUALIZAR); fieldPanel.add(sobrenome);
exit = new JButton(BOTAO_SAIR); fieldPanel.add(cargo);
fieldPanel.add(empresa);
nomeLabel = new JLabel(CONTATO_NOME);
sobrenomeLabel = new JLabel(CONTATO_SOBRENOME); editPanel.add(fieldPanel);
cargoLabel = new JLabel(CONTATO_CARGO);
empresaLabel = new JLabel(CONTATO_EMPRESA); JPanel controlPanel = new JPanel();
controlPanel.add(update);
nome = new JTextField(NOME_COL_WIDTH); controlPanel.add(exit);
sobrenome = new JTextField(SOBRENOME_COL_WIDTH); update.addActionListener(controller);
cargo = new JTextField(CARGO_COL_WIDTH); exit.addActionListener(new ExitHandler());
empresa = new JTextField(EMPRESA_COL_WIDTH);
setLayout(new BorderLayout());
JPanel editPanel = new JPanel(); add(editPanel, BorderLayout.CENTER);
editPanel.setLayout(new BoxLayout(editPanel, BoxLayout.X_AXIS)); add(controlPanel, BorderLayout.SOUTH);
}
JPanel labelPanel = new JPanel();
labelPanel.setLayout(new GridLayout(0, 1 ));

1 2 3 4 5 6 7 8 9 10
Controlador (Controller) Implementação
public Object getUpdateRef() { return update; }
public String getNome() { return nome.getText(); }
public String getSobrenome() { return sobrenome.getText(); }
public String getCargo() { return cargo.getText(); }
public String getEmpresa() { return empresa.getText(); }
public void atualizaContatoView(String newNome, String newSobrenome,
String newCargo, String newEmpresa) {
nome.setText(newNome);
sobrenome.setText(newSobrenome);
cargo.setText(newCargo);
empresa.setText(newEmpresa);
}
private class ExitHandler implements ActionListener{
public void actionPerformed(ActionEvent event){
System.exit(0);
}
}
}

1 2 3 4 5 6 7 8 9 10
Controlador (Controller) Implementação
Passo 6: implemente a classe ContatoEditController private void updateModel(){
String nome = null;
String sobrenome = null;
Esta classe faz o controle entre as informações do
ContatoModel e a ContatoEditView. if (isAlphabetic(view.getNome())) { nome = view.getNome(); }
if (isAlphabetic(view.getSobrenome())) { sobrenome = view.getSobrenome(); }
model.updateModel( nome, sobrenome, view.getCargo(), view.getEmpresa());
}
import java.awt.event.ActionEvent; private boolean isAlphabetic(String input){
import java.awt.event.ActionListener; char [] testChars = {'1 ', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
for (int i = 0; i < testChars.length; i++){
public class ContatoEditController implements ActionListener{ if (input.indexOf(testChars[i]) != -1 ){
private ContatoModel model; return false;
private ContatoEditView view; }
}
public ContatoEditController(ContatoModel m, ContatoEditView v){ return true;
model = m; }
}
view = v;
}
public void actionPerformed(ActionEvent evt){
Object source = evt.getSource();
if (source == view.getUpdateRef()) { updateModel(); }
}

1 2 3 4 5 6 7 8 9 10
Controlador (Controller) Implementação
import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent;
Passo 6: implemente a classe RunController import javax.swing.JFrame; import javax.swing.JPanel;
public class RunController {
Esta classe vai criar o modelo, além criar e
apresentar as duas views. public static void main(String[] args) {
System.out.println("Criando ContatoModel");
Note que o método createGui (que cria as janelas) ContatoModel model = new ContatoModel();
e WindowCloseManager (que fecha as janelas) só System.out.println("Criando ContatoEditView and ContatoEditController");
existem aqui nesta classe. ContatoEditView editorView = new ContatoEditView(model);
model.addContactView(editorView);
createGui(editorView, "Contato Edit Window");
System.out.println("Criando ContatoDisplayView");
ContatoDisplayView displayView = new ContatoDisplayView();
model.addContactView(displayView);
createGui(displayView, "Contato Display Window");
}
private static void createGui(JPanel contents, String title){
JFrame applicationFrame = new JFrame(title);
applicationFrame.getContentPane().add(contents);
applicationFrame.addWindowListener(new WindowCloseManager());
applicationFrame.pack();
applicationFrame.setVisible(true);
}
private static class WindowCloseManager extends WindowAdapter{
public void windowClosing(WindowEvent evt){ System.exit(0); }
}
}

1 2 3 4 5 6 7 8 9 10
Alta Coesão (High Cohesion) Caso de Uso e
Diagrama Conceitual

Caso de Uso
Esse padrão de projetos diz que cada classe deve possuir uma única responsabilidade. Desse modo o código torna­
se mais claro e reutilizável. A desvantagem é que teremos um número de classes maior que teríamos sem o padrão. A
alta coesão significa que cada classe terá suas responsabilidades altamente focadas.

Diagrama Conceitual

banco

Extrato Conta Historico HistoricoItem


conta Contém saldo Contém lista de itens Contém tipo de operação
codigo valor
historico

1 2 3 4 5 6 7 8 9 10
Alta Coesão (High Cohesion) Implementação

Passo 1: implemente a classe Extrato package altacoesaoexemplo;


public class ContaBancaria {
Esta classe faz apenas uma coisa: gera o extrato private double saldo;
private int codigo;
contendo o historico da conta bancária. private Historico historico;
public ContaBancaria(int codigo){
Passo 2: implemente a classe ContaBancaria this.codigo = codigo;
this.historico = new Historico();
}
Esta classe guarda os dados da conta: um código, public int getCodigo() { return codigo; }
o saldo e o histórico. public double getSaldo(){ return saldo; }
public Historico getHistorico() { return historico; }
public void sacar(double valor){
package altacoesaoexemplo; System.out.println("Sacando dinheiro da conta...");
if (this.saldo >= valor){
public class Extrato { this.saldo -= valor;
private ContaBancaria conta; this.historico.adicionarItem(new HistoricoItem("Saque",valor));
} else {
public Extrato(ContaBancaria conta){ System.out.println("Erro. Saldo insuficiente.");
this.conta = conta; }
} }
public void gerarExtrato(){ public void depositar(double valor){
conta.getHistorico().mostrarItens(); System.out.println("Depositando valor na conta...");
} this.saldo += valor;
} this.historico.adicionarItem(new HistoricoItem("Deposito",valor));
}
}

1 2 3 4 5 6 7 8 9 10
Alta Coesão (High Cohesion) Implementação

Passo 3: implemente a classe Historico Passo 4: implemente a classe HistoricoItem


Esta classe faz apenas uma coisa: controla o Esta classe guarda os dados de um item do
historico da conta bancária. histórico (deposito ou saque).

package altacoesaoexemplo; package altacoesaoexemplo;


import java.util.ArrayList; public class HistoricoItem {
private String operacao;
public class Historico { private double valor;
private ArrayList<HistoricoItem> historico;
public HistoricoItem(String operacao, double valor){
public Historico() { this.historico = new ArrayList<>(); } this.operacao = operacao;
this.valor = valor;
public void adicionarItem(HistoricoItem item) { this.historico.add(item); } }
public void mostrarItens(){ public String getOperacao() { return operacao; }
System.out.println("Historico de operacoes: \n"); public double getValor() { return valor; }
for (HistoricoItem item : historico) { @Override
System.out.println(item); public String toString() { return operacao + "\n " + valor + "\n"; }
}
} }
}

1 2 3 4 5 6 7 8 9 10
Alta Coesão (High Cohesion) Implementação

Passo 5: implemente a classe AltaCoesaoTeste


Esta classe faz apenas uma coisa: controla o
historico da conta bancária.

package altacoesaoexemplo;
public class AltaCoesaoTeste {
public static void main(String[] args) {
ContaBancaria conta = new ContaBancaria(1 );
conta.depositar(1 000);
conta.sacar(1 00);
conta.sacar(200);
conta.getHistorico().mostrarItens();
}
}

1 2 3 4 5 6 7 8 9 10
Baixo Acoplamento (Low coupling) Caso de Uso

Caso de Uso
O acoplamento é uma medida de quão fortemente um elemento está conectado, tem conhecimento ou depende de
outros elementos O baixo acoplamento é um padrão avaliativo que determina como atribuir responsabilidades para
apoiar:
­ menor dependência entre as classes
­ mudança em uma classe tendo um impacto menor em outras classes
­ maior potencial de reutilização.

As vantagens sâo:
­ Visando o baixo acoplamento, você pode facilmente fazer alterações nas partes internas dos módulos sem se
preocupar com seu impacto em outros módulos do sistema.
­ O baixo acoplamento também facilita o projeto, a escrita e o teste de código, pois nossos módulos não são
interdependentes uns dos outros
­ Obtemos o benefício de módulos de fácil reutilização e composição.
­ Os problemas também são isolados em unidades de código pequenas e independentes.

1 2 3 4 5 6 7 8 9 10
Baixo Acoplamento (Low coupling) Implementação

Veja implementação do padrão "Alta Coesão"


A implementação do padrão "Alta Coesão" serve para explicar o padrão "Baixo Acoplamento".
Inclusive, estes dois conceitos andam tão juntos que em alguns livros são citados como um só padrão.

1 2 3 4 5 6 7 8 9 10
Indireção (Indirection) Caso de Uso e
Diagrama Conceitual

Caso de Uso
O indirection é uma técnica poderosa para desacoplar unidades de software através da introdução de uma
camada entre duas unidades/camadas existentes, permitindo a introdução de novos comportamentos,
sem prejudicar diretamente as unidades existentes.

Diagrama Conceitual

MODIFICAÇÃO DE INTERFACE:
O indirection é útil para alterar uma interface de destino. Isso é
muitas vezes chamado de "encapsulamento". O encapsulamento
permite que o destino encapsulado seja utilizado mesmo quando
sua interface não corresponde a uma esperada do cliente.
O indirection é uma técnica poderosa para desacoplar unidades
de software através da introdução de uma camada entre duas
unidades/camadas existentes, permitindo a introdução de novos
comportamentos, sem prejudicar diretamente as unidades existentes.

1 2 3 4 5 6 7 8 9 10
Indireção (Indirection) Diagrama Conceitual

ENCAPSULAMENTO DE COMPLEXIDADE:
Algumas implementações podem ser tão complexas que a
compreensão é quase impossível ). O indirection pode ser útil aqui
movendo a complexidade para outra camada, podemos reduzir a
complexidade do código e promover a manutenção.

ENCAPSULAMENTO DE TECNOLOGIA:
Indirection promove acoplamento fraco; permitindo que as
tecnologias sejam mais facilmente intercambiáveis. Isso permite
melhor capacidade de evolução e vida útil estendida do produto.

EXTENSÃO COMPORTAMENTAL:
Indirection é comumente usada para adicionar comportamentos
não necessariamente pertencentes ao cliente ou alvo Esses
comportamentos são normalmente adicionados sem conhecimento
direto de nenhuma das partes.
Esta solução promove a Reutilização e Flexibilidade reduzindo o
acoplamento tornando cada unidade disponível em mais
contextos.

1 2 3 4 5 6 7 8 9 10
Indireção (Indirection) Diagrama Conceitual

antes depois

PedidoItem PedidoItem <<interface>>


ProdServ
produto prodserv Contém
quantidade quantidade

imprimeItem getNome
imprimeProddServ

Contém
Produto Produto Servico
nome nome nome
preço preço preço

1 2 3 4 5 6 7 8 9 10
Indireção (Indirection) Implementação
Passo 1: crie as classes Produto e Servico
Nossa loja recebe pedidos de produtos, mas a partir de agora vai trabalhar com serviços também.
Para que tudo no sistema continue funcionando, criamos a interface ProdServ, que vai ligar os itens dos pedidos, que
com os produtos e serviços ao mesmo tempo.

public class Produto implements ProdServ { public class Servico implements ProdServ {
private String nome; private String nome;
private double preco; private double preco;
public Produto(String n, double p) public Servico(String n, double p)
{ {
nome = n; nome = n;
preco = p; preco = p;
} }
public String getNome() { return nome; } public String getNome() { return nome; }
public double getPreco() { return preco; } public double getPreco() { return preco; }
public void setNome(String newNome) { nome = newNome; } public void setNome(String newNome) { nome = newNome; }
public void setPreco(double newPreco) { preco = newPreco; } public void setPreco(double newPreco) { preco = newPreco; }
public void imprimeProdServ() { public void imprimeProdServ() {
System.out.println("Produto: " + "\tNome: " + nome + " " + "\tPreco: " + preco + "\n"); System.out.println("Produto: " + "\tNome: " + nome + " " + "\tPreco: " + preco + "\n");
} }
} }

1 2 3 4 5 6 7 8 9 10
Indireção (Indirection) Implementação
Passo 2: crie a interface ProdServ Passo 3: crie a classe PedidoItem
A interface vai conter os métodos que serão chamados, Observe que esta classe não vai mais se referir
sem que se saiba se se trata de um Produto ou de um diretamente ao Produto, mas à interface ProdServ, e
Serviço. assim indiretamente aos produtos e serviços.

public interface ProdServ { public class PedidoItem{


private ProdServ prodserv;
public String getNome(); private int quantidade;
public void imprimeProdServ();
} public PedidoItem(ProdServ ps, int q) {
prodserv = ps;
quantidade = q;
}
public ProdServ getProdServ() { return prodserv; }
public int getQuantidade() { return quantidade; }
Indireção public void setProdServ(ProdServ newProdServ) { prodserv = newProdServ; }
public void setQuantidade(int newQuantidade) { quantidade = newQuantidade; }
= public void imprimeItem() {
System.out.println("Item: " + "\tNome: " + prodserv.getNome() + " " + "\tQuantidade: " +
quantidade + "\n");
Indiretamente }
}

1 2 3 4 5 6 7 8 9 10
Indireção (Indirection) Implementação
Passo 4: crie a classe RunIndirection para testar

public class RunIndirection {


public static void main(String[] args) {
System.out.println("Criando um produto");
Produto produto1 = new Produto("Shampo",1 5.00);
produto1 .imprimeProdServ();
System.out.println("Criando um serviço");
Servico servico1 = new Servico("limpeza",250.00);
servico1 .imprimeProdServ();
System.out.println("Criando um item de pedido que é um produto");
PedidoItem item1 = new PedidoItem(produto1 ,1 00);
item1 .imprimeItem();
System.out.println("Criando um item de pedido que é um serviço");
PedidoItem item2 = new PedidoItem(servico1 ,2);
item2.imprimeItem();
} }

1 2 3 4 5 6 7 8 9 10
Polymorphism (Polimorfismo) Caso de Uso e
Diagrama Conceitual

O Polimorfismo se manifesta de duas formas: a


banco
Sobrecarga e a Sobreescrita.
A Sobrecarga acontece quando temos o mesmo Pessoa
método implementado várias vezes, com o mesmo nome
nome, na mesma classe. cpf

A Sobreescrita acontece quando temos o apresentação


método de uma classe pai reescrita, com o mesmo
nome, na classe filha.
A essência do polimorfismo consiste em: em vez de Cliente Funcionario
perguntar a um objeto qual é o seu tipo e então profissão salario

chamar algum método baseado nessa resposta,


você simplesmente chama o método. O ambiente Sobreescrita calcComissao
apresentação Sobrecarga
se encarrega de definir qual método deve ser calcComissao(x)
apresentação
executado.

1 2 3 4 5 6 7 8 9 10
Polymorphism (Polimorfismo) Implementação
Veja a implementação de Reescrita e Sobrecarga que estão no livro 01

1 2 3 4 5 6 7 8 9 10
Pure fabrication Caso de Uso
Um programador criou um software para uma loja, ele atribuiu a responsabilidade de salvar a venda no banco de
dados em uma classe chamada Venda, uma vez que ela conhece os dados da venda.

Considerando os seguintes pontos:

­ Salvar um objeto no Banco de Dados implica em uma série de operações não relacionadas ao conceito de venda;

­ A classe Venda tem de ser associada a interface do banco de dados;

­ Várias outras classes no projeto terão de fazer a mesma coisa.

O Pure Fabrication implementa uma classe chamada de Service. As Services serão responsáveis pela lógica de
negócio da sua aplicação, além de ser responsável por se comunicar com as camadas mais internas do Software,
como por exemplo, uma camada de Dados.

As classes Services são muito usadas para criar as operações de CRUD.

CRUD é o acrônimo para Create (criar), Read (ler), Update (atualizar) e Delete (apagar).

1 2 3 4 5 6 7 8 9 10
Pure fabrication Diagrama Conceitual

banco

Pure Venda VendaItem Produto


Fabrication
VendaItem produto nome
Persistencia quantidade preco
Possui Possui
inserirObjetoBanco calcularTotal calcularSubtotal
atualizarObjeto mostrarItens

1 2 3 4 5 6 7 8 9 10
Pure fabrication Implementação
Passo 1: crie a classe Produto Passo 2: crie a classe VendaItem
Nossa loja vende produtos através de vendas com
vários itens.

package purefabricationexemplo;
package purefabricationexemplo;
public class VendaItem {
public class Produto { private Produto produto;
private String nome; private int quantidade;
private double preco;
public VendaItem(Produto produto, int quantidade) {
public Produto(String nome,double preco){ this.produto = produto;
this.nome = nome; this.quantidade = quantidade;
this.preco = preco; }
}
public Produto getProduto() { return produto; }
public String getNome() { return nome; } public int getQuantidade() { return quantidade; }
public double getPreco() { return preco; }
public double calcularSubtotal() { return this.produto.getPreco()*this.quantidade; }
@Override
public String toString() { return "Produto: " + nome + "\nPreco: " + preco; } @Override
} public String toString() { return produto + "\nQuantidade: " + quantidade; }
}
}

1 2 3 4 5 6 7 8 9 10
Pure fabrication Implementação
Passo 3: crie a classe Venda Passo 4: crie a classe PureFabricationPersistencia
Os itens são agrupados em uma venda via lista. Esta classe será utilizada para simular um Service.

package purefabricationexemplo; Este serviço serve para realizar a persistência dos


objetos Venda.
import java.util.ArrayList;
public class Venda {
private ArrayList<VendaItem> dados = new ArrayList<>();
package purefabricationexemplo;
public void adicionarItem(VendaItem item){ dados.add(item); }
public class PureFabricationPersistencia {
public double calcularTotal(){
double total = 0; public void inserirObjetoBanco(Object objeto){
System.out.println("Inserindo objeto recebido no banco...");
for(VendaItem item : dados){ total += item.calcularSubtotal(); } }
return total; public void atualizarObjeto(Object objeto){
} System.out.println("Atualizando objeto no banco...");
}
public void mostrarItens() {
for (VendaItem item : dados) { }
System.out.println("|-----------------\n");
System.out.println(item);
System.out.println("Subtotal: "+item.calcularSubtotal());
}
System.out.println("|-----------------\nTotal: "+this.calcularTotal());
}
}

1 2 3 4 5 6 7 8 9 10
Pure fabrication Implementação
Passo 5: crie a classe Teste que executa a
package purefabricationexemplo;
implementação
public class Teste {
public static void main(String[] args) {
A maneira normal de implementar a questão está
Produto p1 = new Produto("Batata",0.3);
representada pela linha onde o objeto venda1 Produto p2 = new Produto("Cenoura",0.25);
chamada o método armazenarNoBanco(), que está Venda venda1 = new Venda();
como comentário. venda1 .adicionarItem(new VendaItem(p1 ,1 25));
venda1 .adicionarItem(new VendaItem(p2, 1 00));
A seguir, vemos a maneira utilizando o Pure Fabrication, venda1 .mostrarItens();
onde criamos um objeto db que cuida dos métodos //venda1 .armazenarNoBanco();
CRUD do sistema. PureFabricationPersistencia db = new PureFabricationPersistencia();
db.inserirObjetoBanco(venda1 );
}
}

1 2 3 4 5 6 7 8 9 10
Protected variations Caso de Uso
Variações Protegidas tratam do problema de
atribuir responsabilidades de forma que as
variações que possam ocorrer não tenham efeitos
indesejáveis sobre outros elementos do sistema.
Para implementar o padrão siga os passos:
Passo 1: procure de perto por elementos com
acoplamento direto e que estejam propensos a
mudanças
Passo 2: Identifique os objetos ou pontos do
código que possam ter muitas variações
Passo 3: Crie uma interface estável em torno
desses objetos, protegendo de variações.

1 2 3 4 5 6 7 8 9 10
Protected variations Diagrama conceitual e
Implementação

A ser implementado pelos alunos.

1 2 3 4 5 6 7 8 9 10
Referências
Artigo ­ Desenvolvimento com qualidade com GRASP ­ Site Devmedia
Apresentação ­ Padrões GRASP ­ Sílvio Bacalá Júnior ­ Facom UFU
Pesquisa Controller, Information Expert, Polymorphism ­ Daniel Borba Guimães da Costa / Thais
Resende Araújo Borges Bonfim
Código do Projeto Controller adaptado do exemplo da Sun Microsystems
Pesquisa Creator, High Coesion, Pure Fabrication ­ David Gabriel B. Jorge / Murillo Sampaio
Oliveira Antonio
Pesquisa Indirection, Protected variations ­ Otavio Augusto Ribeiro Araujo e Victor Barcelos Pires
de Souza

Krita Inkscape Scribus Pexels Gimp Synfig Audacity Kdenlive

1 2 3 4 5 6 7 8 9 10
Só mais uma coisa...

@laertecoutinho1

O professor não é dono do saber.


O professor é um organizador de conhecimento, que tem a experiência adequada
para selecionar o que é relevante neste mar infinito de informação.

1 2 3 4 5 6 7 8 9 10
marco@iftm.edu.br

@marcomacielpro Produzido em 2022

Você também pode gostar