Desenho de Software 31
Padrões de Desenho
Motivação inicial trabalho do arquitecto
Christopher Alexander
“A Pattern Language” publicado em 1997 Descreveu o uso de padrões em edifícios e áreas urbanas
Definiu o conceito de padrão como “uma solução para um problema num contexto” Os padrões podem ser aplicados a áreas diversas, incluindo desenvolvimento de software
Padrões de Desenho
Os padrões capturam a essência das
soluções bem sucedidas aos problemas
que recorrentemente surgem quando
se desenvolvem programas
Para além da solução, os padrões
descrevem o problema e o contexto em
que o problema surge
Desenho de Software 33
Conteúdo de um Padrão de Desenho
Nome
Conciso e com significado
Permite a comunicação entre desenhadores
Problema
Define o problema e o contexto
Descreve as condições necessárias
Solução
Descreve os elementos que formam o padrão
Realça os relacionamentos, responsabilidades e
colaborações entre os vários elementos
Consequências
Prós e contras da utilização do padrão
Impacto na extensibilidade, portabilidade e reutilização
Template para Padrões
Nome e classificação Intenção Motivação Aplicabilidade Estrutura Participantes Colaborações Consequências Implementação Exemplo Utilização usual Padrões relacionados
Desenho de Software 35
Padrões de Desenho
Vantagens
Capturam o conhecimento
Facilitam a comunicação entre a equipa de desenvolvimento
Incrementam a reutilização de desenhos bem sucedidos
Facilitam modificações, a produção de documentação e inteligibilidade
Padrão de Desenho
Proxy
Providenciar um representante ou
substituto (
Proxy
) para um objecto
com vista a controlar o acesso ao objecto quando o objecto não pode ser acedido pelos meios normais
O objecto real:
realiza o trabalho
Desenho de Software 37
Exemplo
Motivação
Uma razão para controlar o acesso a
um objecto é adiar o custo total de
criação e inicialização, até ao momento
em que é realmente necessário utilizar
esse objecto
Desenho de Software 39
Exemplo
Editor de documentos que permite ter
objectos gráficos embebidos nos documentos
Restrições:
objectos gráficos podem ser imagens grandes dispendiosas de criar
a abertura de um documento deve ser rápida
deve-se evitar criar todos os objectos na abertura
6ROXomR criar os objectos dispendiosos apenas por pedido (on demand), ou seja, apenas quando a imagem se torna visível
Problemas da Solução
O que colocar no documento em
substituição da imagem real?
Como esconder o facto da imagem ser
criada por pedido para não complicar a
implementação do editor?
Desenho de Software 41
Solução:
Proxy de Imagem
Usar outro objecto:
XP3UR[\ GH
LPDJHP
O
Proxy
actua como substituto da
imagem real
Proxy de Imagem
O
Proxy
de imagem é responsável por:
criar a imagem real quando o editor pede para a exibir, invocando a operação de desenho (Draw)
enviar pedidos subsequentes directamente para a imagem. Para tal, PDQWpPXPD
UHIHUrQFLDSDUDDLPDJHPUHDO
responder a pedidos relativos ao tamanho, sem ter que instanciar a imagem. Por esse motivo, guarda o tamanho da imagem (altura e largura)
Desenho de Software 43
Diagrama de Classes
Aplicabilidade
Sempre que houver necessidade de ter uma referência mais versátil e sofisticada para um objecto do que um simples ponteiro
Situações comuns:
3UR[\ UHPRWR- providencia um representante local de um objecto que se encontra num espaço de endereçamento diferente
3UR[\ YLUWXDO - cria objectos dispendiosos
apenas por pedido
3UR[\ GHSURWHFomR- controla o acesso ao
objecto original
6PDUW5HIHUHQFH - é um substituto de um ponteiro básico que, executa acções adicionais, quando o objecto é acedido (locks, load)
Desenho de Software 45
Forças
Substituir um objecto por um Proxy, ou
vice-versa, deve ser transparente
Desenho de Software 47
Participações
Subject (Graphic)
define uma interface comum para o RealSubject e
o Proxy. Desta forma, o Proxy pode ser usado sempre que é esperado o RealSubject
Proxy (ImageProxy)
mantém uma referência que lhe permite aceder
ao objecto real (RealSubject)
fornece uma interface idêntica ao Subject, para que o Proxy possa ser substituído pelo
RealSubject
controla o acesso ao objecto real e pode ser responsável pela sua criação e destruição
Participações/Colaborações
RealSubject (Image)
define o objecto real que o Proxy
representa
&RODERUDo}HV
Proxy envia pedidos ao RealSubject
quando apropriado
Desenho de Software 49
Consequências
Introduz um nível de indirecção no acesso a um objecto
A indirecção tem muitas utilizações:
3UR[\ UHPRWR – permite esconder o facto do objecto residir num espaço de endereçamento distinto
3UR[\ YLUWXDO – permite executar optimizações como a criação de um objecto por pedido
3UR[\ GHSURWHFomRe 6PDUW5HIHUHQFHV –
permite a execução de tarefas adicionais de
arrumação (housekeeping), quando o objecto é
acedido
Consequências
Geralmente, os objectos reais não
acedem aos seus
Proxies
Como o
Proxy
e o objecto têm a
mesma interface, podem ser facilmente
substituídos um pelo outro
Desenho de Software 51
Refactorização do Desenho
Desenvolver programas úteis hoje e
reutilizáveis amanhã
Fase de Prototipagem
Prototipar na primeira passagem
Fase de Expansão
Expandir o protótipo inicial
Fase de Consolidação
Construir agregações a partir de herança
Criar super-classes abstractas
Criar classe vazia
Criar variável de instância
Prototipar na Primeira Passagem
Contexto
Resolver inicialmente o problema
Obter resultados, ainda que parciais, rapidamente
Problema
Não se constróem programas a partir do “zero”
É difícil saber com exactidão quais são os requisitos
É difícil desenhar imediatamente um programa
que resolve os requisitos e está preparado para resistir à mudança
Desenho de Software 53
Prototipar na Primeira Passagem
Solução
O desenho inicial deve resolver os requisitos conhecidos
Os programas devem “crescer‘” não serem
“construídos”
Utilizar programas existentes
Programar por diferença
Estratégias
Substantivos implicam objectos, verbos implicam
operações
Reutilizar objectos existentes por herança
Correr agora, e melhorar mais tarde
Evitar generalidade prematura
Expandir o Protótipo Inicial
Contexto
Programas bem sucedidos raramente são
estáticos mas terão que evoluir
Problema
O programa é aplicado em novas
situações, pelo que é necessário proceder a alterações
As alterações levam à diminuição da
qualidade do programa: estrutura e inteligibilidade.
Desenho de Software 55
Expandir o Protótipo Inicial
Solução
Localizar as alterações em novas subclasses
Resulta uma hierarquia que modela uma
história de alterações
Estratégias
Derivar de código existente em vez de o
modificar
Reutilizar objectos existentes por herança
Correr agora, e melhorar mais tarde Evitar generalidade prematura
Consolidar
Contexto
À medida que os objectos evoluem começa-se a esclarecer
como é que se pode melhorar o desenho
Problema
Programas reutilizáveis raramente surgem após uma
primeira etapa de análise
Objectos têm a capacidade de se alterar de forma controlada: novas funcionalidades e melhorar a qualidade
Variações encontram-se nas folhas da hierarquia
Solução
Refactorizar o programa
Aumentar a generalidade e integridade estrutural dos
Desenho de Software 57
Agregações a Partir de Herança
Contexto
A hierarquia de classes que surge após a prototipagem e expansão possui as
funcionalidades necessárias mas não é elegante nem reutilizável
Devem ser seguidos os seguintes
princípios de desenho:
Factorizar diferentes implementações em
sub-componentes
Separar métodos que não comunicam
Enviar mensagens para componentes em vez
de para this
Agregações a Partir de Herança
Problema
A herança é utilizada extensivamente nas
fases de prototipagem e expansão pois:
A herança é suportada ao nível da linguagem
Não é claro se um relacionamento é is-aou
has-aaté que as subclasses se tornem mais estáveis
A herança facilita a partilha de operações e
variáveis, white box, possibilitando obter
Desenho de Software 59
Agregações a Partir de Herança
Consequências
A transformação de herança em agregação tem alguns benefícios:
A coesão e a capsulação podem ser
melhoradas por se dividir uma classe em duas
Agregados podem mudar as sua componentes
em tempo de execução explorando o polimorfismo
Classes separadas podem ser reutilizadas e
evoluir de forma independente
Um agregado pode ter mais do que uma
instância de um dado componente
Criar Super-Classes Abstractas
Contexto
A hierarquia de classes que surge após a
prototipagem e expansão possui as funcionalidades necessárias mas não é elegante nem reutilizável
Devem ser seguidos os seguintes
princípios de desenho:
Hierarquias de classes devem ser profundas
O topo da hierarquia de classes deve abstracto
Desenho de Software 61
Criar Super-Classes Abstractas
Problema
A mesma abstracção pode surgir em diferentes partes do programa pois:
Uma prática comum é estender um programa por cópia
e alteração
Diferentes membros da equipa podem implementar a
mesma funcionalidade
Solução
Suponha-se que as classes C1 e C2 partilham uma abstracção:
Adicionar uma nova classe A1 Fazer A1 super-classe de C1 e C2
Determinar o comportamento comum a C1 e C2
Alterar C1 e C2 de modo a que a implementação da sua
parte comum seja a mesma
Mover as funções comuns para A1 e apagar de C1 e C2
Criar Super-Classes Abstractas
Consequências
A consolidação de abstracções tem os
seguintes benefícios:
Reduz o tamanho do programa
A separação de abstracções melhora a
inteligibilidade e a reutilização
Limita a propagação de erros no código,
Desenho de Software 63
Introduzir Objecto Null
Técnica de re-factorização para
substituir muitas ocorrências de
if (customer == null) plan = BillingPan.basic(); else plan = customer.getPlan();
por
plan = customer.getPlan();Desenho da Solução
Desenho de Software 65
Procedimento
1. Criar uma subclasse da classe original e
introduzir o comportamento nulo
1. Criar a operação isNull() em ambas as classes
(retorna trueapenas na subclasse)
2. Compilar
3. Em todos os locais onde se retorna um null
deve-se passar a retornar o objecto null
4. Em todos os locais que comparam uma
variável da classe original com null substituir com uma invocação a isNull()
5. Compilar e testar
Procedimento
6. Identificar todos os casos em que se invoca
uma operação se o objecto for diferente de null e se executa um comportamento
alternativo se for null
7. Para cada um dos casos identificados acima
redefine-se a operação da classe null com o comportamento alternativo
8. Remover a condição de verificação para
cada caso em que se usa o comportamento
Desenho de Software 67
Exemplo
class Site... Customer getCustomer() { return _customer; } Customer _customer; class Customer...public String getName() {...} public BillingPan getPlan() {...}
public PaymentHistory getHistory() {...} public class PaymentHistory...
int getWeeksDelinquentInLastYear() String customerName;
if (customer == null) customerName = “occupant”; else customerName = customer.getName();
Exemplo
class NullCustomer extend Customer { public boolean isNull() {
return true; }
}
class Customer...
public boolean isNull() { return false;
}
protected Customer() {}
// clients do need to not know about null class static Customer newNull() {
return new NullCustomer(); }
Desenho de Software 69
Exemplo
class Site...
Customer getCustomer() { return (_customer == null) ?
Customer.newNull(): _customer;
}
Customer customer = site.getCustomer(); BillingPlan plan;
if (customer.isNull()) plan = BillingPlan.basic(); else plan = customer.getPlan();
String customerName;
if (customer.isNull()) customerName = “occupant”; else customerName = customer.getName();
! "
Exemplo
class NullCustomer... public String getName() {
return “occupant”; }
public Plan getPlan() { return BillingPlan.basic(); }
String customerName = customer.getName(); Plan customerPlan() = customer.getPlan();