Desenho de Software
António Rito Silva
Sumário
Caracterização
Objectivos Problemas QualidadesTécnicas
Desenho de Software 2Técnicas
Avaliação e Validação
Casos Notáveis
Exemplo
Conclusões
Objectivos
Desenho é o processo da passagem
do espaço do problema para o espaço
da solução. À descrição da solução
também se chama desenho.
O desenho descreve uma solução
para o problema que satisfaz os seus
para o problema que satisfaz os seus
requisitos
note-se que muitas outras soluções satisfazendo os mesmo requisitos são possíveis
Arquitectura e Desenho
O processo de desenho deve considerar
dois aspectos:
Descrever para os clientes o que o sistema faz - Arquitectura de Software
Descreve as funções do sistema
Está relacionado com os documentos de requisitos
Desenho de Software 4
Descrever para a equipa de
desenvolvimento como o sistema faz -Desenho de Programas
Uma descrição dos principais algoritmos
Decomposição
O processo de desenho usa sempre alguma forma de decomposição. Existem duas
estratégias de decomposição:
Desenho Funcional em que o estado do
sistema está centralizado e é partilhado pelas funções que manipulam esse estado
Desenho com Objectos em que o sistema é
Desenho com Objectos em que o sistema é visto como um conjunto de objectos que
encapsulam a informação que manipulam.
Os clientes têm usualmente uma visão mais funcional do sistema...
Problemas
Porquê este desenho?
Dado um conjunto de requisitos vários desenhos são possíveis
Abordagens top-down versus
bottom-up
Desenho de Software 6
Propagação de alterações
A evolução do software pode obrigar a um re-desenho
Não existe uma única decomposição
Qualidades
Independência
Coesão LigaçãoInteligibilidade
Integridade
Integridade
Adaptabilidade
Componentes Independentes
Um desenho de qualidade facilita oentendimento, a implementação, os testes, as alterações e as adaptações do desenho. Mais ainda, facilita o seguimento dos
requisitos no desenho
A independência entre componentes é uma qualidade do desenho que permite algumas
Desenho de Software 8
qualidade do desenho que permite algumas das propriedades acima
Para medir a independência entre
componentes usam-se duas medidas:
Coesão intra-componente
Coesão Intra-Componente
Um componente é coeso se todos os
seus elementos estão envolvidos na
satisfação dos objectivos do
componente
Quando não existe coesão, ao
modificar uma determinada função é
modificar uma determinada função é
necessário procurar em todos os
componentes as partes relativas à
função
O tipo de coesão determina a
localidade das alterações
Coesão de Objecto
Cada operação fornece a
funcionalidade que permite que os
atributos do objecto sejam
modificados, inspeccionados e
usados, numa base de
disponibilização de serviços
Desenho de Software 10
Ligação Inter-Componentes
Dois componentes dizem-se fortemente ligados quando existe uma grande
dependência entre eles
Dois componentes dizem-se fracamente ligados quando existe uma fraca
dependência entre eles
Dois componentes dizem-se desligados
Dois componentes dizem-se desligados quando são completamente independentes
A ligação entre componentes depende de: As referências entre componentes
Os dados passados entre componentes
O controlo entre componentes
Níveis de Ligação
Ligação de Conteúdo – verifica-se
quando um componente conhece a
estrutura interna de outro
componente
Qualquer alteração interna pode ter
repercussões nos restantes componentes
Desenho de Software 12
repercussões nos restantes componentes
Ligação de Partilha – verifica-se
quando vários componentes
partilham um conjunto de dados
Todos os componentes dependem da estrutura dos dados partilhados, por exemplo, variáveis globais
Níveis de Ligação
Ligação de Controlo – verifica-se quando um componente passa parâmetros que
controlam a lógica de outro componente
O componente controlado não pode funcionar independentemente do componente controlador
Ligação de Estrutura – verifica-se quando uma estrutura de dados é usada para
uma estrutura de dados é usada para passar informação entre componentes
A formatação e organização dos dados gera uma dependência entre os componentes
Ligação de Dados – verifica-se quando dados são passados entre componentes
Inteligibilidade
Influência a inteligibilidade:
Coesão e Ligação – o componente pode ser entendido sem referir outros
componentes
Nomes – os nomes têm significado e reflectem entidades do espaço do
problema e do espaço da solução
Desenho de Software 14
problema e do espaço da solução
Documentação – a documentação
justifica e relaciona o desenho com o espaço do problema
Complexidade – grau de encapsulamento dos algoritmos complexos
Adaptabilidade
A qualidade que determina a
facilidade de adaptação a novos
requisitos
Requer:
Ligação fraca
Abstracções estáveis
Self-contained – um componente
constituído por interfaces fornecidas e interfaces requeridas
Técnicas
Estilos arquitecturais
Desenho funcional
Desenho com objectos
Padrões de desenho
Refactorização do desenho
Desenho de Software 16Refactorização do desenho
Técnicas de aperfeiçoamento do
desenho
Arquitecturas de Software
A arquitectura associa os requisitosidentificados no sistema com os
componentes que os vão implementar
Uma arquitectura de software é constituída por dois elementos básicos:
Componentes que definem as computações
Conectores que descrevem as interacções entre
Conectores que descrevem as interacções entre componentes
Um estilo arquitectural define uma família de sistemas com o mesmo padrão de
organização estrutural:
Vocabulário de tipos componente e conector
Estilos Arquitecturais
Camadas
Repositórios
...
Camadas
Criptografia
Interface ficheiros
Interface ficheiros
Gestão de chaves
Autenticação
Camadas: Propriedades
VantagensDesenvolvimento incremental pois o desenho possui vários níveis de abstracção
Evolução pois a alteração de uma camada
apenas vai afectar as camadas imediatamente acima e abaixo
Reutilização pois permitem diferentes implementações de uma camada
Desenho de Software 20
implementações de uma camada
Desvantagens
Por vezes é difícil estruturar o problema em níveis de abstracção
Problemas de desempenho devido à coordenação entre as camadas
Repositórios
Repositório Aplicação 1 Aplicação 2 Aplicação 3 Repositório (dados partilhados) Aplicação 6 Aplicação 5 Aplicação 4Repositórios: Propriedades
Exemplos Base de dados Ambientes de desenvolvimento Blackboard VantagemConstitui uma arquitectura aberta uma vez que
Desenho de Software 22
Constitui uma arquitectura aberta uma vez que a representação dos dados está acessível a
diversos fabricantes de componentes
Desvantagem
Dependência dos dados partilhados pois estes devem estar numa representação aceitável para a todos os componentes
Desenho Funcional
Baseado na decomposição do sistema
num conjunto de funções
As funções partilham o estado global
do sistema que se encontra
centralizado
As funções podem possuir estado
local, mas apenas durante a duração
da sua execução
Desenho Funcional
Os detalhes dos algoritmos estão nas
funções mas o estado não está
escondido pelo que:
A alteração do estado global por uma função pode ter efeitos colaterais com um impacto indesejável noutras funções
Desenho de Software 24
um impacto indesejável noutras funções
As funções têm mais tendência a serem alteradas que os dados
Desenho com Objectos
Princípios
Encapsulação – restrição no acesso à representação interna
Abstracção – eliminação do irrelevante e amplificação do essencial
Modularidade – construção à custa de
Modularidade – construção à custa de elementos de menor granularidade
Desenho com Objectos
Primitivas
Objecto – agrega os dados e as operações que os manipulam
Classe – especifica um conjunto de objectos, a sua interface, estrutura e implementação
Desenho de Software 26
implementação
Herança – uma classe pode ser definida como extensão ou restrição de outra
Polimorfismo – uma variável pode, em tempo de execução, referir objectos de classes diferentes
Desenho com Objectos
Objectivos – o resultado final do
desenvolvimento deve ter as
seguintes qualidades:
Reutilização – capacidade de ser
reutilizado parcialmente ou como um todo noutros programas
todo noutros programas
Extensibilidade – facilidade de adaptação às alterações de especificação
de modo a se alcançar o princípio do
Aberto/Fechado
Desenho com Objectos
Herança versus Delegação
reutilização white-box versus black-box
estática (tempo de compilação) versus dinâmica (tempo de execução)
encapsulação
número de objectos
Desenho de Software 28
Padrões de Desenho
Verifica-se que:
Existem problemas que ocorrem recorrentemente
É possível identificar classes de soluções
As soluções reflectem as forças em conflito
conflito
Padrões de Desenho
Considere-se o jogo de xadrez. Para
se ser um bom jogador de xadrez é
necessário:
Aprender os movimentos permitidos, como termina o jogo,... (primitivas)
Saber que se deve ocupar centro do
Desenho de Software 30
Saber que se deve ocupar centro do tabuleiro (princípios)
Estudar os jogos dos grandes mestres (padrões)
Padrões de Desenho
Os padrões descrevem uma cultura
de desenvolvimento de programas,
permitindo desta forma a reutilização
de conhecimento
Descrevem soluções que evoluíram
ao longo do tempo
ao longo do tempo
Têm como motivação inicial o
trabalho do arquitecto Christopher
Alexander
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
Desenho de Software 32
Para além da solução, os padrões
descrevem o problema e o contexto
em que o problema surge
Padrões de Desenho
Propriedadescapturam o conhecimento da experiência
fornecem um vocabulário e conhecimento comuns
estimulam a reutilização de conhecimento
são os blocos de construção de uma arquitectura
fornecem um esquema pré-definido para
implementação, descrevendo as suas partes, as suas colaborações e responsabilidades
identificam e dão nome a abstracções que estão acima das classes e das instâncias
Padrão de Desenho Proxy
Fornecer 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
Desenho de Software 34
O objecto real:
realiza o trabalho
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
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
Desenho de Software 36
a abertura de um documento deve ser rápida
deve-se evitar criar todos os objectos na abertura
Solução: 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
Solução:
Proxy de Imagem
Usar outro objecto: um Proxy de
imagem
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, mantém uma referência para a imagem real
referência para a imagem real
responder a pedidos relativos ao tamanho, sem ter que instanciar a imagem. Por esse motivo, guarda o tamanho da imagem
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:
Proxy remoto - providencia um representante
Proxy remoto - providencia um representante local de um objecto que se encontra num espaço de endereçamento diferente
Proxy virtual - cria objectos dispendiosos apenas por pedido
Proxy de protecção - controla o acesso ao objecto original
Forças
Substituir um objecto por um Proxy,
ou vice-versa, deve ser transparente
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)
Desenho de Software 44
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
Colaborações
Proxy envia pedidos ao RealSubject quando apropriado
Consequências
Introduz um nível de indirecção no acesso a um objecto
A indirecção tem muitas utilizações:
Proxy remoto – permite esconder o facto do objecto residir num espaço de endereçamento distinto
Desenho de Software 46
Proxy virtual – permite executar optimizações como a criação de um objecto por pedido
Proxy de protecção – 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
Refactorização do Desenho
Desenvolver programas úteis hoje e
reutilizáveis amanhã
Prototipar na primeira passagem
Expandir o protótipo inicial
Consolidar estrutura
Desenho de Software 48
Construir agregações a partir de herança
Prototipar na Primeira Passagem
ContextoResolver inicialmente o problema
Obter resultados, ainda que parciais, rapidamente
Problema
Não se constróem programas a partir do “zero”
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
Prototipar na Primeira Passagem
SoluçãoO desenho inicial deve resolver os requisitos conhecidos
Os programas devem “crescer” e não serem “construídos”
Utilizar programas existentes
Programar por diferença
Desenho de Software 50
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
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 situações, pelo que é necessário proceder a alterações
As alterações levam à diminuição da qualidade do programa: estrutura e inteligibilidade.
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
Desenho de Software 52
Estratégias
Derivar de código existente em vez de o modificar
Reutilizar objectos existentes por herança
Correr agora, e melhorar mais tarde
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
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
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:
Desenho de Software 54
princípios de desenho:
Factorizar diferentes implementações em
sub-componentes
Separar métodos que não comunicam
Enviar mensagens para componentes em
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 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
Agregações a Partir de Herança
Solução
Factorizar parte de uma classe numa nova classe. Suponha-se que A é uma subclasse de C:
Adicionar uma instância de C como variável
de instância de A
Desenho de Software 56
de instância de A
Mudar as referências para as variáveis e
funções herdadas de C por referências para a variável de instância
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
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
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
Desenho de Software 58
Devem ser seguidos os seguintes princípios de desenho:
Hierarquias de classes devem ser profundas
O topo da hierarquia de classes deve
abstracto
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 implementar a mesma funcionalidade
Criar Super-Classes Abstractas
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
Desenho de Software 60
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 inteligibilidade e a reutilização
Limita a propagação de erros no código,
Introduzir Objecto Null
Técnica de re-factorização para
substituir muitas ocorrências de
if (customer == null) plan = BillingPan.basic(); else Desenho de Software 62 plan = customer.getPlan();
por
plan = customer.getPlan();Procedimento
1. Criar uma subclasse da classe original e introduzir o comportamento nulo
1. Criar a operação isNull() em ambas as classes (retorna true apenas na subclasse)
2. Compilar
3. Em todos os locais onde se retorna um null
Desenho de Software 64
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()
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 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
Exemplo
class Site... Customer getCustomer() { return customer; } Customer customer; class Customer...public String getName() {...}
public BillingPan getPlan() {...}
Desenho de Software 66
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;
return false; }
protected Customer() {}
// clients do need to not know about null class static Customer newNull() {
return new NullCustomer(); }
Exemplo
class Site...
Customer getCustomer() {
return (customer == null) ? Customer.newNull(): customer;
}
Customer customer = site.getCustomer(); BillingPlan plan;
Desenho de Software 68
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(); String customerName = customer.getName();
Plan customerPlan() = customer.getPlan();
Aperfeiçoamento do Desenho
Desenho por contrato
Protótipos de desenho
Desenho por Contrato
Utiliza-se a técnica de desenho por contrato para
assegurar que o desenho satisfaz a sua especificação
Um componente, dito cliente, que necessita dos
serviços de outro componente, dito fornecedor, pelo qual celebram um contrato que especifica:
as obrigações mútuas, chamados de pré-condições
os benefícios, chamados de pós-condições
as restrições de coerência, chamadas de invariantes
Facilitam o desenvolvimento separado, a reutilização, os testes, a verificação da coerência entre as
cláusulas, a verificação da correcção da
Desenho por Contrato
fill is require in_valve.open out_valve.closed ... code ... ensure in_valve.closed out_valve.closed is_full Desenho de Software 72 is_full end invariantProtótipos de Desenho
Permitem verificar se a solução de
desenho vai de facto resolver o
problema
O protótipo deve focar apenas no
aspecto de desenho sobre o qual
existem dúvidas
existem dúvidas
Validação e Verificação
Validação – certificar se o desenho
satisfaz todos os requisitos do utilizador
Verificação – certificar se a solução incorpora as qualidades de desenho requeridas
Existem várias técnicas:
Validação Matemática pode ser difícil mas muito
Desenho de Software 74
Validação Matemática pode ser difícil mas muito útil para alguns aspectos críticos
Métricas de Desenho permitem quantificar a qualidade
Comparação de Desenhos para avaliar os compromissos
Métricas de Desenho
Não se pode gerir aquilo que não se controla e não se pode controlar aquilo que não se consegue
medir. Tom DeMarco
As seguintes métricas propostas por Robert Martin medem a qualidade do desenho com objectos.
Considera módulos reutilizáveis de componentes:
Ligação de Fora/Dentro (LFD): número de classes de fora
Ligação de Fora/Dentro (LFD): número de classes de fora do módulo que dependem de classes de dentro do módulo
Ligação de Dentro/Fora (LDF): número de classes de dentro do módulo que dependem de classes fora do módulo
Instabilidade (I): I = LDF / (LFD + LDF), I=0 indica estabilidade e I=1 indica instabilidade do módulo
Métricas de Desenho
Princípio do Aberto/FechadoUm sistema não pode ter todos os módulos estáveis
Um módulo deve ser aberto à extensão e fechado à modificação
Desta forma, os módulos estáveis devem ser muito abstractos enquanto que os módulos instáveis devem ser muito concretos
Desenho de Software 76
instáveis devem ser muito concretos
Uma medida de abstracção:
Abstracção (A): A = número de classes
abstractas do módulo / número total de classes do módulo, A=0 significa que é concreto e A=1 que é abstracto
Métricas de Desenho
É desejável que os módulos estejam próximos da sequência principal Distância (D): D = |(A+I-1) / 2| 1 (0,1) instabilidade 1 (1,0)Comparação de Desenhos
Uma especificação possibilita muitosdesenhos
Deve-se construir diversas soluções para o problema usando diferentes estilos
arquitecturais
Decidir qual o melhor desenho para os objectivos do sistema
Tabelas de comparação definem para cada
Desenho de Software 78
Tabelas de comparação definem para cada atributo de qualidade se um particular
estilo arquitectural o suporta ou não
Tabela de pesos indica qual a prioridade que cada atributo de qualidade tem para o sistema a desenvolver, permite calcular um valor para cada estilo arquitectural
Documentação do Desenho
Devem ser apresentadas quais as
razões do desenho que indiquem os
aspectos críticos e os compromissos
da solução
Utilizar as notações necessárias para
exprimir o desenho e as suas
exprimir o desenho e as suas
Casos Notáveis
Padrão Data Access Object
http://java.sun.com/blueprints/corej2eepatterns/Patt erns/DataAccessObject.html
Padrão Intercepting Filter
http://java.sun.com/blueprints/corej2eepatterns/Patt erns/InterceptingFilter.html
Moldura de Objectos JUnit
Desenho de Software 80
Moldura de Objectos JUnit
Erich Gamma and Kent Beck
http://junit.sourceforge.net/doc/cookstour/cookstour .htm
Padrão Arquitectural Modelo-Vista-Controlador
In Pattern-Oriented Software Architecture: A System of Patterns. Frank Buschmann et al.
Padrão Data Access Object
Contexto
Acesso aos dados depende da fonte dos dados
tipo de armazenamento
Problema
As aplicações podem usar a API JDBC mas mesmo num SGBD relacional a sintaxe e o formato dos comando SQL pode variar
devido ao particular produto
Existe ainda maior variação quando se usam diferentes tipos de SGBD
Desenho de Software 82
usam diferentes tipos de SGBD
Cria-se uma dependência entre o código da aplicação e o código de acesso aos dados que dificulta a migração entre diferentes
tipos de SGBD e diferentes produtos de um mesmo tipo
Forças
Componentes aplicacionais
necessitam de obter/guardar
informação de/para um suporte
persistente
As APIs dos suportes persistentes
variam dependendo do vendedor do
variam dependendo do vendedor do
produto e do tipo do produto
Componentes aplicacionais
usualmente usam APIs proprietárias
A portabilidade dos componentes
aplicacionais é afectada
Solução
Utilizar um objecto de acesso aos
dados (DAO) para abstrair e
encapsular todos os acessos à fonte
de dados
O DAO gere a ligação à fonte de
dados para obter e guardar dados
Desenho de Software 84
dados para obter e guardar dados
O DAO funciona como um adaptador
entre o componente e a fonte de
Participantes e Colaborações
Estratégia
Estratégia
Abstract Factory e Factory Method
Código: Fábrica Abstracta
package ServidorPersistente;
public interface ISuportePersistente { public void iniciarTransaccao()
throws ExcepcaoPersistencia;
public void confirmarTransaccao() throws ExcepcaoPersistencia;
public void cancelarTransaccao() throws ExcepcaoPersistencia; Desenho de Software 90 throws ExcepcaoPersistencia; public IItemPersistente getIItemPersistente(); public ISeccaoPersistente getISeccaoPersistente(); public ISitioPersistente getISitioPersistente(); }
Código: Fábrica Concreta
package ServidorPersistente.OJB;
public class SuportePersistenteOJB implements ISuportePersistente {
Implementation _odmg = null; Database _db = null;
...
public IItemPersistente getIItemPersistente() getIItemPersistente()
{ return new ItemOJB(); } public ISeccaoPersistente
getISeccaoPersistente()
{ return new SeccaoOJB(); } public ISitioPersistente
getISitioPersistente()
Código: Interface DAO
package ServidorPersistente; import Dominio.ISitio;
public interface ISitioPersistente { ISitio readByNome(String nome)
throws ExcepcaoPersistencia;
Desenho de Software 92
void write(ISitio sitio)
throws ExcepcaoPersistencia; void delete(ISitio sitio)
throws ExcepcaoPersistencia; void deleteAll()
throws ExcepcaoPersistencia; }
Código: Implementação OJB
package ServidorPersistente.OJB; public class SitioOJB
extends ObjectFenixOJB
implements ISitioPersistente { public SitioOJB() { }
...
public void deleteAll()
throws ExcepcaoPersistencia {
String oqlQuery = "select all from " + Sitio.class.getName();
super.deleteAll(oqlQuery); }
Código: Objecto Valor
package Dominio;
public class Sitio implements ISitio { private String _nome;
private int _anoCurricular; private int _semestre;
private String _departamento; private String _curso;
private List _seccoes;
Desenho de Software 94
private List _seccoes;
// códigos internos da base de dados private int _codigoInterno;
...
// getter e setter methods ...
Consequências
Permite a transparência
Facilita a migração
Reduz a complexidade do código do
componente aplicacional
Centraliza os acessos a dados numa
Centraliza os acessos a dados numa
camada separada
Acrescenta uma camada adicional
Necessita do desenho de uma
hierarquia de classes
Padrão Intercepting Filter
O mecanismo de tratamento de
pedidos da camada de apresentação
trata pedidos muito diferentes entre
si, o que implica tratamento
especializado para cada um deles
Alguns pedidos têm tratamento
Desenho de Software 96
Alguns pedidos têm tratamento
imediato, enquanto outros
necessitam de ser modificados ou
verificados antes de serem
Problema
É necessário haver pré- e
pós-processamento dos pedidos feitos por
um cliente Web e das respectivas
respostas
O cliente foi autenticado?
A sessão do cliente é válida?
A sessão do cliente é válida?
O tipo de browser do cliente é suportado?
Forças
Deve haver processamento comum a
todos os pedidos e respostas
Centralização da lógica comum
A adição e remoção dos componentes
de processamento deve ser
Desenho de Software 98
de processamento deve ser
independente
Solução
Criar filtros para processar os
serviços comuns de forma que:
Interceptem os pedidos que
chegam e as respostas que são
enviadas
enviadas
Seja possível adicionar ou remover
os filtros de forma discreta sem ser
necessário modificar o código já
Estrutura
Participantes
GestorFiltrosGere o processamento dos filtros
Cria a CadeiaFiltros com os filtros apropriados, na ordem correcta, e inicia o processamento
CadeiaFiltros
Conjunto ordenado de filtros independentes
Filtro1, Filtro2, Filtro3
Desenho de Software 102
Filtro1, Filtro2, Filtro3
Filtros individuais que são mapeados para um alvo
A CadeiaFiltros coordena o seu processamento
Alvo
Custom Filter (1/3)
Custom Filter
Definido pelo programador
Dois exemplos alternativos:
Utilização do Decorator para encapsular o
processamento dos pedidos dentro dos filtros
filtros
Utilização de Gestor de Filtros e Cadeia de
Filtros para coordenar o processamento dos filtros
Custom Filter (2/3)
Exemplo – Utilização do Decorator
Desenho de Software 104 Problema
Se alterarmos a forma de tratar o pedido, tem que se alterar também o código dos filtros e do
Custom Filter (3/3)
Exemplo – Utilização de Gestor e Cadeia de Filtros
Standard Filter
Standard Filter
Os filtros são controlados declarativamente, utilizando um ficheiro de configuração
Este ficheiro inclui o mapeamento dos filtros para URL’s específicos
Quando um cliente faz um pedido a que
corresponde aquele URL mapeado, os filtros são
Desenho de Software 106
corresponde aquele URL mapeado, os filtros são processados por ordem antes que o alvo do
pedido seja invocado <filter>
<filter-name> StandardEncodeFilter </filter-name> ...
</filter> ...
<filter-mapping>
<filter-name> StandardEncodeFilter </filter-name> <url-pattern> /EncodeTestServlet </url-pattern> </filter-mapping>
Outras Estratégias
Base Filter
Superclasse comum a todos os filtros utilizados
Partilhada por todos os filtros
Encapsula funcionalidades comuns
Template Filter
Template Filter
Base Filter que contém uma estrutura fixa a que todos os filtros têm que
obedecer
Consequências
Centraliza o controlo com fraca
ligação entre filtros
Promove a reutilização
Configuração independente e
declarativa
Desenho de Software 108
declarativa
Moldura de Objectos
Uma moldura de objectos é um desenho
reutilizável que modela parte de um sistema de software e é descrito por um conjunto de classes abstractas e pela forma como as suas instâncias colaboram
Uma moldura de objectos resulta sempre de uma análise do domínio
Uma boa moldura de objectos pode reduzir o Uma boa moldura de objectos pode reduzir o
custo de desenvolvimento de uma ordem magnitude pois permite a reutilização de desenho e código.
O desenvolvimento de uma boa moldura de
objectos é difícil pois tem de ser simples para ser fácil de aprender e facilmente utilizável, e
Moldura de Objectos Junit em UML
Um exemplo de utilização do UML
como forma de comunicar desenho
Dá ênfase ao que é relevante
Não substitui o código
A Moldura de Testes
Moldura de Objectos JUnit
Objectivos
Simplificar a construção de testes de
modo a que os programadores se sintam motivados a escrever testes
Escrever testes que sejam perenes e
sejam utilizáveis por outras pessoas para
Desenho de Software 114 sejam utilizáveis por outras pessoas para além do criador
Seja possível combinar testes feitos por diferentes pessoas
Utilizar testes para construir novos testes
Caso de Teste
Tornar cada caso de teste num
objecto de modo a simplificar a sua
manipulação
O padrão Comando “encapsula um
pedido como um objecto permitindo
listar pedidos, guardar pedidos, ...”
listar pedidos, guardar pedidos, ...”
Caso de Teste
Desenho de Software 116
public abstract class TestCase implements Test { private final String fName;
public TestCase(String name) { fName= name;
}
public abstract void run(); …
Parte Fixa
Como separo a parte fixa de um teste
da parte de teste
O padrão Método Matriz “define um
esqueleto de algoritmo permitindo
que algumas das etapas surjam em
subclasses mas preservando-se a
subclasses mas preservando-se a
estrutura do algoritmo”
Parte Fixa
public void run() {
Desenho de Software 118
public void run() { setUp();
runTest(); tearDown(); }
protected void runTest() { }
protected void setUp() { }
protected void tearDown() { }
Resultados do Teste
Pretende-se registar o resultado das
falhas e um sumário condensado dos
sucessos
Resultados do Teste
public class TestResult extends Object { protected int fRunTests;
public TestResult() { fRunTests= 0;
} }
public void run(TestResult result) { result.startTest(this); setUp(); Desenho de Software 120 runTest(); tearDown(); }
public synchronized void startTest(Test test) { fRunTests++;
Faltas dos Testes
Existem dois tipos de faltas: falhas e
erros. Os primeiros podem ser
antecipados. Em ambos os casos
pretende-se que os testes continuem
a executar
Faltas dos Testes
public void run(TestResult result) { result.startTest(this); setUp(); try { runTest(); } catch (AssertionFailedError e) { result.addFailure(this, e); } catch (Throwable e) { result.addError(this, e); Desenho de Software 122 result.addError(this, e); } finally { tearDown(); } }
protected void assert(boolean condition) { if (!condition)
throw new AssertionFailedError(); }
Faltas dos Testes
É necessário registar as faltas em
TestResult
public synchronized void addError(Test test, Throwable t) { fErrors.addElement(new TestFailure(test, t));
}
public synchronized void addFailure(Test test, Throwable t) { fFailures.addElement(new TestFailure(test, t));
}
public class TestFailure extends Object { protected Test fFailedTest;
protected Throwable fThrownException; }
Proliferação de Classes
A aplicação do padrão Comando a
cada caso de teste leva a uma
proliferação de classes, uma para
cada teste
Como juntar os casos de teste numa
única classe e serem uniformes do
Desenho de Software 124
única classe e serem uniformes do
ponto de vista do invocador dos
testes?
O padrão Adaptador “converte a
interface de uma classe numa outra
que é esperada pelo cliente”
Proliferação de Classes
Padrão Adaptador
public class TestMoneyEquals extends MoneyTest {
public TestMoneyEquals() { super("testMoneyEquals"); } protected void runTest () { testMoneyEquals(); }
}
Ou utiliza-se reflexão do JAVA
protected void runTest() throws Throwable {
Desenho de Software 126 protected void runTest() throws Throwable {
Method runMethod= null; try {
runMethod= getClass().getMethod(fName, new Class[0]); } catch (NoSuchMethodException e) {
assert("Method \""+fName+"\" not found", false); }
try {
runMethod.invoke(this, new Class[0]); }
// catch InvocationTargetException and IllegalAccessException }
Composição de Casos de Teste
Necessita-se executar diversos testes
e de modo a que o resultado é escrito
no mesmo TestResult
O padrão Composição “compõe
objectos em árvores que representam
hierarquias todo-parte permitindo
hierarquias todo-parte permitindo
tratar uniformemente os objectos
individuais e as composições”
Composição de Casos de Teste
Desenho de Software 128
public interface Test {
public abstract void run(TestResult result); }
public class TestSuite implements Test { private Vector fTests= new Vector(); }
Composição de Casos de Teste
public void run(TestResult result) {
for (Enumeration e= fTests.elements(); e.hasMoreElements(); ) { Test test= (Test)e.nextElement();
test.run(result); }
}
public void addTest(Test test) { fTests.addElement(test); }
public static Test suite() {
TestSuite suite= new TestSuite();
suite.addTest(new MoneyTest("testMoneyEquals")); suite.addTest(new MoneyTest("testSimpleAdd")); }
public static Test suite() {
return new TestSuite(MoneyTest.class); }
Moldura de Objectos JUnit
Padrão Arquitectural MVC
O padrão arquitecturalModelo-Vista-Controlador (MVC) divide uma aplicação interactiva em três componentes: modelo, vista e controlador
O modelo contém o núcleo da funcionalidade e dos dados
As vistas mostram a informação ao utilizador
As vistas mostram a informação ao utilizador
Os controladores tratam das entradas dos utilizadores
As vistas e os controladores formam a
interface. Um mecanismo de propagação
das alterações assegura a coerência entre a interface utilizador e o modelo
Exemplo
Problema
As interfaces utilizador são muito passíveis de sofrerem pedidos de alteração
Diferentes utilizadores têm requisitos
diferentes, e por vezes conflituosos, sobre como deve ser a interface utilizador
Um sistema que satisfaça os requisitos acima deve permitir:
acima deve permitir:
A mesma informação ser apresentada de forma diferente em distintas janelas
A visualização e comportamento da aplicação reflecte imediatamente as alterações aos dados
Alterações à interface são fáceis e possíveis em tempo de execução
Solução
Existem três tipos de componentes: modelo, vista e controlador
O modelo encapsula o cerne dos dados e da
funcionalidade, é independente das representações de saída e do comportamento associado às entradas
A vista mostra os dados ao utilizador, obtém os dados do modelo e permite a existência diferentes
Desenho de Software 134
dados do modelo e permite a existência diferentes vistas do modelo
O controlador recebe os eventos de entrada que converte para pedidos de serviços ao modelo e às vistas
A separação entre o modelo a vista e o controlador permite múltiplas vistas do mesmo modelo
Estrutura
Model coreData : undefined attach() detach() notify() getData() Observer update() update() coreData : undefined attach() detach() notify() getData() 1 * getData() service() View initialize() makeController() activate() display() update() Controller initialize() handleEvent() update() initialize() makeController() activate() display() update() initialize() handleEvent() update() getData() service() 1 0..1Dinâmica: Propagação
: C o n t r o l l e r : M o d e l : V i e w h a n d l e E v e n t s e r v i c e n o t i f y u p d a t e g e t D a t a Desenho de Software 136 u p d a t e g e t D a t aDinâmica: Inicialização
: M o d e l : V i e w m a i n p r o g r a m : c r e a t e c r e a t e i n i t i a l i z e a t t a c h m a k e C o n t r o l l e r c r e a t e : C o n t r o l l e r m a k e C o n t r o l l e r c r e a t e i n i t i a l i z e a t t a c h s t a r t E v e n t P r o c e s s i n gConsequências
Vantagens
Múltiplas vistas do mesmo modelo
Sincronização das vistas
Troca dinâmica de vistas e controladores
Alteração do look and feel
Potencial para moldura de objectos
Desvantagens
Aumento da complexidade
Desenho de Software 138
Possível excesso do número de propagação de alterações
Ligação forte entre as vistas e os respectivos controladores
Ligação forte das vistas e controladores com o modelo
Ineficiência dos acessos aos modelos por parte dados das vistas
Alteração da vista e do controlador quando é necessário portar
Exemplo
Páginas WEB
Baseadas em html
scripts – comandos que interpretam
o pedido e retornam html (mais
usado para os pedidos)
usado para os pedidos)
server pages – o programa está
estruturado no contexto da página
de texto que é retornada (mais
Exemplo
MVC na WEB
Combina pedidos como scripts e respostas como server pages
Modelos separados da apresentação
Conclusões
P62 – Relacionar o Desenho com os
Requisitos
É necessário saber que requisitos são satisfeitos por cada componente
P63 – Avaliar Alternativas
Enumerar um conjunto de arquitecturas
Desenho de Software 142
Enumerar um conjunto de arquitecturas e analisar cada uma delas de acordo com os objectivos
Alguns métodos de desenho têm como resultado arquitecturas específicas,
nessa situação deve-se seleccionar o método mais adequado
P65 - Encapsular
P66 – Não Reinventar a Roda
Reutilizar ideias, componentes, técnicas, ...
P67 – Não Complicar
Existem duas formas de construir um
Conclusões
Existem duas formas de construir um desenho de software. Uma forma é fazer o desenho tão simples que é
óbvio não ter deficiências. Uma outra
forma é fazer o desenho tão complicado que não tem deficiências óbvias. C. A. R. Hoare
P68 – Evitar Muitos Casos
Especiais
Se existirem provavelmente foi
porque se definiram as
abstracções ou os algoritmos
errados
Conclusões
Desenho de Software 144errados
Conclusões
P69 – Minimizar a Distância
Intelectual
A distância entre o problema e a
solução
Pessoas diferentes percebem
Pessoas diferentes percebem
diferentes estruturas quando
analisam o mesmo problema
Conclusões
P71 – Manter a Integridade do
Desenho
Usar um número limitado de
“formas” de desenho
Alterar as “formas” estabelecidas
Desenho de Software 146
Alterar as “formas” estabelecidas
apenas para introduzir mais
elegância ou simplicidade
P73 – Usar Ligação Fraca e
P74 – Desenhar para as
Alterações
O desenho deve ser:
Modular
Portável
Conclusões
Portável
Flexível
Com a menor distância intelectual
Compreensível
Conclusões
P77 – Introduzir Generalidade no
Software
Componentes genéricos podem cumprir o seu objectivo em diversas situações sem terem de ser alterados
Componentes genéricos são mais difíceis de desenhar e normalmente possuem
Desenho de Software 148
Componentes genéricos são mais difíceis de desenhar e normalmente possuem
um menor desempenho, mas:
São ideais quando uma função semelhante
tem que ser executada em vários sítios
Permitem maior reutilização sem
modificação
Reduzem as despesas de manutenção
devido ao reduzido número de componentes
P78 – Introduzir Flexibilidade no
Software
Componentes flexíveis podem ser facilmente modificados para se
adaptarem a uma nova situações
Componentes flexíveis são mais difíceis
Conclusões
Componentes flexíveis são mais difíceis de desenhar, mas:
Possuem um maior desempenho que os
componentes genéricos
Permitem maior reutilização que os
Referências
Pfleeger01, Capítulo 5, excepto 5.2 e 5.4.
David95, Alguns princípios do Capítulo 4.
Erich Gamma et al. Bridge Design Pattern. In Gamma95.
Brian Foote and William Opdyke. Lifecycle and Refactoring Patterns That Support
Desenho de Software 150
and Refactoring Patterns That Support
Evolution and Reuse. In Pattern Languages of Program Design 1995. Capítulo 14.
http://www.laputan.org/pub/foote/evolution. pdf
Referências
Martin Fowler. Introduce Null Object. In
Refactoring: Improving the Design of Existing Code 1999.
http://www.refactoring.com/catalog/introduc eNullObject.html
Robert Martin. OO Design Quality Metrics: An Analysis of Dependencies.
Analysis of Dependencies.
http://www.objectmentor.com/publications/o odmetrc.pdf
Deepak Alur et al. Data Access Object pattern
http://java.sun.com/blueprints/corej2eepatte rns/Patterns/DataAccessObject.html
Referências
Deepak Alur et al. Intercepting Filter pattern
http://java.sun.com/blueprints/corej2eepatte rns/Patterns/InterceptingFilter.html
Martin Fowler. A UML Testing Framework. Software Development OnLine. April 1999.
http://www.ddj.com/dept/architect/1844156 74
Desenho de Software 152 74
Erich Gamma and Kent Beck. JUnit A Cook's Tour.
http://junit.sourceforge.net/doc/cookstour/co okstour.htm
Frank Buschmann et al. Model-View-Controller. In Buschmann96.