Engenharia de Software
Desenho de Software
António Rito Silva
Rito.Silva@inesc-id.pt
Desenho de Software 2Sumário
Caracterização
Objectivos Problemas Qualidades Té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
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
Descrever para a equipa de
desenvolvimento como o sistema faz
-Desenho de Programas
Uma descrição dos principais algoritmos As estruturas e os fluxos de dados
Desenho de Software 5
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 é
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...
Desenho de Software 6
Problemas
Porquê este desenho?
Dado um conjunto de requisitos vários
desenhos são possíveis
Abordagens
top-down
versus
bottom-up
Propagação de alterações
A evolução do software pode obrigar a um
re-desenho
Não existe uma única decomposição
A tirania da decomposição principal
Qualidades
Independência
Coesão Ligação Inteligibilidade
Integridade
Adaptabilidade
Componentes Independentes
Um desenho de qualidade facilita o
entendimento, 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 das propriedades acima
Para medir a independência entre
componentes usam-se duas medidas:
Coesão intra-componente Ligação inter-componentes
Desenho de Software 9
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 é
necessário procurar em todos os
componentes as partes relativas à
função
O tipo de coesão determina a
localidade das alterações
Desenho de Software 10
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
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
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
O nível de complexidade da interface 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
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
Desenho de Software 13
Níveis de Ligação
Ligação de Controlo – verifica-se quando
um componente passa parâmetros que controlam a actividade 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 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
Apenas tipos de dados simples são passados
Desenho de Software 14
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
Documentação – a documentação justifica
e relaciona o desenho com o espaço do problema
Complexidade – grau de encapsulamento
da complexidade dos algoritmos
Adaptabilidade
A qualidade que determina 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
Técnicas de aperfeiçoamento do
Desenho de Software 17
Arquitecturas de Software
A arquitectura associa os requisitos
identificados 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
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 Conjunto de restrições sobre as possíveis
combinações de componentes e conectores
Desenho de Software 18
Estilos Arquitecturais
Camadas
Repositórios
...
Camadas
Utilizadores
Criptografia
Interface ficheiros
Gestão de chaves
Autenticação
Camadas: Propriedades
Vantagens Desenvolvimento 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 Desvantagens
Por vezes é difícil estruturar o problema em níveis
de abstracção
Problemas de desempenho devido à coordenação
Desenho de Software 21
Repositórios
Repositório (dados partilhados) Aplicação 1 Aplicação 6 Aplicação 2 Aplicação 3 Aplicação 5 Aplicação 4 Desenho de Software 22Repositórios: Propriedades
Exemplos Base de dados Ambientes de desenvolvimento Blackboard Vantagem 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
As funções têm mais tendência a serem
Desenho de Software 25
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
elementos de menor granularidade
Desenho de Software 26
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
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
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
Desenho de Software 29
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 Não existem soluções óptimas
Desenho de Software 30
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
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
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
descrevem o problema e o contexto em
que o problema surge
Desenho de Software 33
Padrões de Desenho
Propriedades
capturam 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
documentam a arquitectura dos programas
Desenho de Software 34
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
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
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
Desenho de Software 37
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 38
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
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 41
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
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
Smart Reference- é um substituto de um
ponteiro básico que, executa acções adicionais, quando o objecto é acedido
Desenho de Software 42
Forças
Substituir um objecto por um Proxy, ou
vice-versa, deve ser transparente
Estrutura
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
Desenho de Software 45
Participações/Colaborações
RealSubject (Image)
define o objecto real que o Proxy
representa
Colaborações
Proxy envia pedidos ao RealSubject
quando apropriado
depende do tipo de Proxy
Desenho de Software 46
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
Proxy virtual – permite executar optimizações
como a criação de um objecto por pedido
Proxy de protecção e Smart References –
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
Refactorização do Desenho
Desenvolver programas úteis hoje e
reutilizáveis amanhã
Prototipar na primeira passagem Expandir o protótipo inicial
Consolidar
Construir agregações a partir de herança Criar super-classes abstractas
Criar classe vazia
Desenho de Software 49
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 50
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.
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 53
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
Desenho de Software 54
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-a ou
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 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
Mudar as referências para as variáveis e
funções herdadas de C por referências para a variável de instância
Desenho de Software 57
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 58
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
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
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 61
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 de Software 62Desenho da Solução
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 65
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();
Desenho de Software 66
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(); }
COMPILE AND TEST
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();
COMPILE AND TEST
Exemplo
class NullCustomer... public String getName() {
return “occupant”; }
public Plan getPlan() { return BillingPlan.basic(); }
String customerName = customer.getName();
Plan customerPlan() = customer.getPlan();
Desenho de Software 69
Aperfeiçoamento do Desenho
Desenho por contrato
Protótipos de desenho
Desenho de Software 70
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 implementação, documentam o desenho, ...
Desenho por Contrato
fill is require in_valve.open out_valve.closed ... code ... ensure in_valve.closed out_valve.closed is_full end invariant
is_full = (0.95 * capacity < gauge ) and (gauge <= 1.05 * capacity)
Protó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
Desenho de Software 73
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
útil para alguns aspectos críticos
Métricas de Desenho permitem quantificar a
qualidade
Comparação de Desenhos para avaliar os
compromissos
Desenho de Software 74
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 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/Fechado
Um 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
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| instabilidade ab st racç ão 1 1 (0,1) (1,0) sequê nc ia pr inc ipalDesenho de Software 77
Comparação de Desenhos
Uma especificação possibilita muitos
desenhos
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
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
Desenho de Software 78
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