Escolar Documentos
Profissional Documentos
Cultura Documentos
LIVRO 02
Padrões GRASP
Sumário
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
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 ObjectOriented 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, levase 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
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.
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.
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
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
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. Podese
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
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
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
1 2 3 4 5 6 7 8 9 10
Alta Coesão (High Cohesion) Implementação
1 2 3 4 5 6 7 8 9 10
Alta Coesão (High Cohesion) Implementação
1 2 3 4 5 6 7 8 9 10
Alta Coesão (High Cohesion) Implementação
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
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
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.
1 2 3 4 5 6 7 8 9 10
Indireção (Indirection) Implementação
Passo 4: crie a classe RunIndirection para testar
1 2 3 4 5 6 7 8 9 10
Polymorphism (Polimorfismo) Caso de Uso e
Diagrama Conceitual
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.
Salvar um objeto no Banco de Dados implica em uma série de operações não relacionadas ao conceito de venda;
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.
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
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.
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
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
1 2 3 4 5 6 7 8 9 10
Só mais uma coisa...
@laertecoutinho1
1 2 3 4 5 6 7 8 9 10
marco@iftm.edu.br