1
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
Desenho de Software 32
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
2
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
Desenho de Software 34
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
3
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
Desenho de Software 36
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� fica escondido pelo Proxy
4
Desenho de Software 37
Exemplo
Desenho de Software 38
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
5
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
Desenho de Software 40
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?
6
Desenho de Software 41
Solução: Proxy de Imagem� Usar outro objecto: XP�3UR[\ GH�LPDJHP
� O Proxy actua como substituto da imagem real
Desenho de Software 42
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, PDQWpP�XPD�UHIHUrQFLD�SDUD�D�LPDJHP�UHDO�
� responder a pedidos relativos ao tamanho, sem ter que instanciar a imagem. Por esse motivo, guarda o tamanho da imagem (altura e largura)
7
Desenho de Software 43
Diagrama de Classes
Desenho de Software 44
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[\ GH�SURWHFomR - controla o acesso ao objecto original
� 6PDUW�5HIHUHQFH - é um substituto de um ponteiro básico que, executa acções adicionais, quando o objecto é acedido (locks, load)
8
Desenho de Software 45
Forças� Substituir um objecto por um Proxy, ou
vice-versa, deve ser transparente
Desenho de Software 46
Estrutura
9
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
Desenho de Software 48
Participações/Colaborações� RealSubject (Image)
� define o objecto real que o Proxyrepresenta
� &RODERUDo}HV� Proxy envia pedidos ao RealSubject
quando apropriado� depende do tipo de Proxy
10
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[\ GH�SURWHFomR�e 6PDUW�5HIHUHQFHV –permite a execução de tarefas adicionais de arrumação (housekeeping), quando o objecto é acedido
Desenho de Software 50
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
11
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
Desenho de Software 52
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
12
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
Desenho de Software 54
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.
13
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
Desenho de Software 56
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
objectos
14
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
Desenho de Software 58
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-a ou
has-a até que as subclasses se tornem mais estáveis
� A herança facilita a partilha de operações e variáveis, white box, possibilitando obter rapidamente resultados
15
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
Desenho de Software 60
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 Subclasses devem ser especializações
16
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
Desenho de Software 62
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,
devido a haver menos cópia
17
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();
� porplan = customer.getPlan();
Desenho de Software 64
Desenho da Solução
18
Desenho de Software 65
Procedimento1. Criar uma subclasse da classe original e
introduzir o comportamento nulo1. Criar a operação isNull() em ambas as classes
(retorna true apenas na subclasse)
2. Compilar3. Em todos os locais onde se retorna um null
deve-se passar a retornar o objecto null4. Em todos os locais que comparam uma
variável da classe original com nullsubstituir com uma invocação a isNull()
5. Compilar e testar
Desenho de Software 66
Procedimento6. 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
9. Compilar e testar
19
Desenho de Software 67
Exemploclass 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();
Desenho de Software 68
Exemploclass 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();
}
�������������������! ���"�
20
Desenho de Software 69
Exemploclass 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();
�������������������! ���"�
Desenho de Software 70
Exemploclass NullCustomer...
public String getName() {
return “occupant”;
}
public Plan getPlan() {
return BillingPlan.basic();
}
String customerName = customer.getName();
Plan customerPlan() = customer.getPlan();
�������������������! ���"�