Mapeamento do Projeto para o
Código-Fonte
e Padrões para Atribuição de
Responsabilidades (GRASP)
MAPEAMENTO DO PROJETO PARA
O CÓDIGO-FONTE
Relação com outros artefatos
• O código-fonte é o principal artefato da fase de implementação
• A partir do diagrama de classes de projeto (DCP), chega-se à implementação em código-fonte
• O código-fonte pode ser testado através de testes de unidade e testes de integração
Criando classes, atributos e métodos a
partir do DCP
• Pode ser feito sistematicamente, até mesmo automaticamente
Realização de uma associação
unidirecional, um-para-um
Account
Advertiser 1 1
public class Advertiser { private Account account; public Advertiser() {
account = new Account(); }
public Account getAccount() { return account;
} }
Associação bidirecional um-para-um
public class Advertiser {
/* O atributo account é
* inicializado no construtor * e nunca é modificado. */
private Account account; public Advertiser() {
account = new Account(this); }
public Account getAccount() {
Account
Advertiser 1 1
public class Account {
/* O atributo owner é
* inicializado durante o * o construtor e nunca * é modificado. */
private Advertiser owner;
public Account(owner:Advertiser)
{
this.owner = owner;
}
Associação unidirecional um-para-muitos
public class Advertiser { private Set accounts; public Advertiser() {
accounts = new HashSet(); }
public void addAccount(Account a) { accounts.add(a);
}
public void removeAccount(Account a) {
accounts.remove(a); }
}
public class Account { }
Associação bidirecional um-para-muitos
public class Advertiser { private Set accounts; public Advertiser() {
accounts = new HashSet(); }
public void addAccount(Account a) {
accounts.add(a); a.setOwner(this); }
public void removeAccount(Account a)
{
accounts.remove(a); a.setOwner(null);
public class Account {
private Advertiser owner;
public void setOwner(Advertiser
newOwner) {
if (owner != newOwner) {
Advertiser old = owner; owner = newOwner; if (newOwner != null) newOwner.addAccount(this); if (oldOwner != null) old.removeAccount(this); } } Advertiser 1 * Account
Associação unidirecional muitos-para-muitos
public class Tournament { private List players; public Tournament() {
players = new ArrayList(); }
public void addPlayer(Player p) { if (!players.contains(p)) { players.add(p); } } }
public class Player { }
Associação bidirecional muitos-para-muitos
public class Tournament { private List players; public Tournament() {
players = new ArrayList(); }
public void addPlayer(Player p) {
if (!players.contains(p)) { players.add(p);
p.addTournament(this); }
public class Player {
private List tournaments; public Player() {
tournaments = new ArrayList(); } public void addTournament(Tournament t) { if (!tournaments.contains(t)) { tournaments.add(t); t.addPlayer(this); } Tournament * * Player
- Parta da menos conectada para a mais conectada
- Implemente e teste cada classe antes de passar adiante
SalesLineItem quantity : Integer getSubtotal() ProductCatalog ... getProductDesc(...) ProductDescription description : Text price : Money itemID : ItemID ... Store address : Address name : Text addSale(...) Payment amount : Money 1..* 1..* Register ... endSale() enterItem(...) makeNewSale() makePayment(...) Sale isComplete : Boolean time : DateTime becomeComplete() makeLineItem(...) makePayment(...) getTotal() ... 1 1 1 1 1 1 * 1 2 3 4 5 6 7
PADRÕES PARA ATRIBUIÇÃO DE
RESPONSABILIDADES (GRASP)
O que são padrões (patterns)?
• Princípios e soluções codificadas em um formato estruturado descrevendo um problema e uma solução
• Um par nomeado problema/solução que pode ser aplicado em novos contextos
• É o conselho de projetistas experientes para ajudar novos projetistas em dadas situações
A ideia por trás de padrões de design é simples:
Escreva e catalogue interações comuns entre objetos que programadores acharam
frequentemente úteis.
Resultado:
Facilitar o reuso de código orientado a objetos entre projetos e entre
Características de um bom padrão
• Resolve um problema • É um conceito provado • A solução não é óbvia
• Descreve um relacionamento
• O padrão tem um componente humano significativo
Tipos de padrões
Padrões arquiteturais
Expressa uma organização ou esquema estrutural fundamental para sistemas de software.
Padrões de projeto (Design Patterns)
Proveem um esquema para refinar os subsistemas ou componentes de um sistema de software, ou as
relações entre eles.
Expressões idiomáticas (Idioms)
Uma expressão idiomática descreve como
implementar aspectos específicos de componentes ou das relações entre eles usando as características de
Descrevendo padrões
Nome: O padrão deve ter um nome significativo. Problema: Uma descrição do problema.
Contexto: Explica a aplicabilidade do padrão.
Forças: Uma descrição das forças relevantes e restrições e como
elas interagem/conflitam entre si.
Solução: Relações estáticas e regras dinâmicas descrevendo
como implementar o resultado desejado.
Consequências: Implicações (boas e ruins) do uso da solução. Exemplos: Uma ou mais amostras de aplicações do padrão.
Padrões GRASP
Qual a classe, no caso geral, que é responsável por... • Atribuir uma responsabilidade a uma classe?
• Evitar ou minimizar dependências adicionais?
• Maximizar a coesão e minimizar o acoplamento? • Aumentar o reuso e diminuir a manutenção?
• Maximizar a compreensão? • ... etc.
Padrões GRASP
General Responsibility Assignment Software Patterns
• Expert • Creator • Low Coupling • High Cohesion • Controller • Polymorphism • Pure Fabrication • Indirection • Law of Demeter
Expert
Problema:
Qual o princípio mais básico pelo qual as responsabilidades são atribuídas no design orientado a objetos?
Solução:
Atribua uma responsabilidade à classe que tem a informação necessária para cumprir esta responsabilidade.
Expert : Exemplo
• Quem é responsável por conhecer o total geral de uma venda em uma típica aplicação de ponto de vendas?
Sale date time Sales LineItem quantity Product Specification description price UPC Described-by * Contain s 1.. *
Expert : Exemplo
• A classe necessita de todas as instâncias de SalesLineItem e seus subtotais. Somente Sale conhece isto. Assim, Sale é o expert na informação.
• Portanto: Sale date time total() :Sale t := total() New method
Expert : Exemplo
• Mas subtotais são necessários para cada linha de item (multiplicar quantidade por preço).
• Pelo Expert, SalesLineItem é o especialista, pois conhece quantidade e tem associação com ProductSpecification, que conhece o preço.
Sale date time total() :Sale t := total() sli:SalesLineItem SalesLineItem quantity subtotal() 2: st := subtotal() :Product Specification 2.1: p := price() Product Specification description price UPC SalesLineItem :SalesLineItem 1*: [for each] sli := next()
Expert : Exemplo
Assim, atribua responsabilidades a três classes:
Classe Responsabilidade
Sale Conhece o total da venda
SalesLineItem Conhece o subtotal da linha de item de venda
Expert
• Mantém encapsulamento da informação • Promove baixo acoplamento
• Promove classes altamente coesivas • Pode fazer uma classe tornar-se
Creator
Problema:
A quem atribuir a responsabilidade por criar uma nova instância de uma classe?
Solução:
Determinar que classe deveria criar instâncias da outra classe baseado no relacionamento entre potenciais classes criadoras e a classe a ser instanciada.
Creator
Quem tem a responsabilidade de criar um objeto?
Por creator, atribua à classe B a responsabilidade de criar uma instância da classe A se:
B agrega objetos A B contém objetos A
B registra instâncias de objetos A B usa de perto objetos A
B tem os dados de inicialização para criar objetos A Quando houver opções de escolha, prefira:
Creator : Exemplo
Quem é responsável por criar objetos SalesLineItem?
Procure por uma classe que agregue ou contenha objetos SalesLineItem.
Sale date time Sales LineItem Product Specification Described-* Contain s 1.. *
Creator : Exemplo
O padrão Creator sugere Sale.
Sale date time makeLineItem() total() :Sale makeLineItem(quantity) :SalesLineItem
Creator
• Promove baixo acoplamento tornando
instâncias de uma classe em responsáveis por criar objetos que elas precisam referenciar
• Criando elas próprias os objetos, elas evitam ficar dependentes de outra classe que crie o objeto para elas
Low Coupling
Problema:
Como dar suporte à baixa dependência entre classes e ao aumento do reuso?
Solução:
Atribua responsabilidades de modo que o acoplamento permaneça baixo.
Sobre acoplamento em linguagens OO
Em linguagens orientadas a objetos, formas comuns de acoplamento entre TipoX e TipoY incluem:
• TipoX tem uma atributo (variável de instância) que se refere a uma instância TipoY, ou ao próprio TipoY.
• TipoX tem um método que referencia uma instância do TipoY, ou o próprio TipoY, de algum modo. Estes métodos
tipicamente incluem um parâmetro ou variável local do TipoY, ou o objeto do TipoY retornado de uma mensagem.
• TipoX é uma subclasse direta ou indireta de TipoY.
Low coupling
• Classes ficam mais fáceis de se manter • Mais fáceis de se reusar
Low Coupling
Como podemos tornar classes independentes de outras classes?
Quem tem a responsabilidade de criar um Payment?
Low Coupling
Duas possibilidades: :POST p : Payment :Sale makePayment() 1: create() 2: addPayment(p) 1. POST2. Sale :POST :Sale
:Payment makePayment() 1: makePayment()
1.1. create()
Low coupling sugere Sale porque Sale tem de ser acoplada a Payment de qualquer jeito (Sale conhece o seu total).
High Cohesion
Problema:
Como manter a complexidade gerenciável?
Solução:
Atribua responsabilidades de modo que a coesão permaneça alta.
Sobre coesão em linguagens OO:
• Coesão Muito Baixa: Uma classe é a única
responsável por muitas coisas em muitas áreas funcionais diferentes
• Coesão Baixa: Uma classe é a única responsável por uma tarefa complexa em uma área funcional.
• Coesão Alta: Uma classe tem responsabilidades
moderadas em uma área funcional e colabora com outras classes para realizar tarefas.
High cohesion
• As classes ficam mais fáceis de se manter • Mais fáceis de se compreender
• Frequentemente dá suporte a baixo acoplamento
• Dá suporte ao reuso por causa da
High Cohesion
Quem tem a responsabilidade de criar um Payment?
1.Post
:POST p : Payment
:Sale
makePayment() 1: create()
2: addPayment(p)
Parece OK se makePayment for considerado
isoladamente, mas, adicionando mais operações de sistema, POST teria mais e mais responsabilidades e ficaria menos coesivo.
High Cohesion
Dar a responsabilidade a Sale dá suporte a maior coesão em POST, assim como baixo acoplamento.
:POST :Sale
:Payment
makePayment() 1: makePayment()
Controller
Problema:
A quem atribuir responsabilidade para tratar um evento de sistema?
Solução:
Se um programa recebe eventos de fontes externas além de sua interface gráfica,
adicione uma classe de eventos para desacoplar a(s) fonte(s) de eventos dos objetos que de fato tratam os eventos.
Escolhas de controladores
O padrão Controller dá orientação para escolhas geralmente aceitáveis.
Atribua a responsabilidade para tratar uma mensagem de evento de sistema para uma classe representando uma das seguintes escolhas:
1. O negócio ou a organização geral (um controlador de fachada); 2. O “sistema” geral (um controlador de fachada);
3. Uma coisa animada no domínio que realizaria o trabalho (um controlador de papel);
Benefícios:
• Aumento no potencial de reuso. Usar um objeto controlador mantém fontes de eventos externos e tratadores de eventos internos independentes de seus tipos e comportamento.
• Permite refletir sobre os estados de um caso de uso e assegurar que as operações do sistema ocorrem na sequência determinada.
• Permite refletir sobre o estado atual de uma atividade e as operações dentro do caso de uso.
Controller : Exemplo
Eventos de sistema no caso de uso Comprar Itens entrarItem()
terminarVenda() fazerPagamento()
Controller : Exemplo
Por controller, nós temos quatro escolhas
O sistema geral Post
O negócio geral Store
Alguém no mundo real que
é ativo na tarefa Cashier
Um tratador artificial de todos os eventos
de sistema de um caso de uso BuyItemsHandler
:POST enterItem(upc, quantity) :Store enterItem(upc, quantity) :Cashier enterItem(upc, quantity) :BuyItemsHandler enterItem(upc, quantity)
A escolha de qual utilizar será influenciada por outros fatores tais como coesão e acoplamento
Bom design: camada de apresentação desacoplada do domínio do problema
Object Store
Enter Item End Sale UPC Make Payment Total Quantity Tendered Balance Cashier :POSTCommand presses button onEnterItem() 1: enterItem(upc, qty) Presentation Layer (Command Object)
system event message
Mau design: camada de apresentação acoplada ao domínio do problema
Object Store
Enter Item End Sale UPC Make Payment Total Quantity Tendered Balance Cashier :POSTCommand presses button onEnterItem() :Sale 1: makeLineItem(upc, qty) Presentation Layer (Command object) Domain Layer
It is undesirable for a presentation layer objects such as a Java applet to get involved in deciding how to handle domain processes.
Business logic is embedded in the presentation layer, which is not useful.
POSTApplet should not send this message.
Controller
• Usar um objeto controlador mantém fontes de eventos externos e tratadores de eventos
internos independentes de seus tipos e comportamento
• Os objetos controladores podem se tornar
altamente acoplados e sem coesão com mais responsabilidades