MAC 413/5715 - Tópicos de Programação Orientada a
Objetos
Aula 6 - 26/08/2002
Padrões POSA I e POSA II
Pattern-Oriented Software Architecture
POSA I: A System of Patterns
POSA II: Patterns for Networked and Concurrent Objects
Padrões Alexandrinos:
Nome
Discussão técnica: quais as questões/forças
envolvidas
PORTANTO: uma solução comum para estas questões/forças
C. Alexander et al. A Pattern Language - Towns.Buildings.Construction
. Oxford University Press, 1977.
Exemplo: Window Place
Contexto: todos nós adoraramos nos sentar próximos
a uma janela e apreciar a paisagem. Uma sala sem janelas que podem ser apreciadas
raramente nos faz sentir confortáveis.
Problema: se uma sala contém uma janela perto da qual não
podemos sentar, uma pessoa nessa sala vai ser puxada por duas forças
antagônicas:
ela quer sentar e se sentir confortável
ela é atraída para perto da janela
Solução: obviamente, se não há um lugar
próximo à janela onde se pode sentar confortavelmente, não
há como resolver este conflito.
PORTANTO: em qualquer sala na qual as pessoas passam um certo tempo
durante o dia, faça com que pelo menos uma janela seja um lugar
para se sentar.
Diagrama.
Ao escrever um padrão, veja se você tem bem claro 3 coisas
que devem estar contidas na descrição do padrão:
Contexto: uma situação que leva a um problema
Problema: um problema recorrente que acontece em determinada situação
Solução: uma solução comprovada para
problema
Os padrões do POSA I se dividem em 3 grupos:
Architectural Patterns (alto nível)
Da lama à estrutura
Layers
Pipes and Filters
Blackboard
Sistemas Distribuídos
Broker
Sistemas Interativos
Model-View-Controller
Presentation-Abstraction-Control
Sistemas Adaptáveis
Microkernel
Reflection
Design Patterns (nível médio)
Decomposição Estrutural
Whole-Part
Organização de Trabalho
Master-Slave
Controle de Acesso
Proxy
Gerenciamento
Command Processor
View Handler
Comunicação
Forward-Receiver
Client-Dispatch-Server
Publisher-Subscriber
Idioms (baixo nível)
Counted Pointer
Cartões CRC (Class, Responsibility, Collaborator)
Exemplo, padrão Camadas:
Class
Camada J
Responsibility
Provê serviços usados pela camada J+1
Delega subtarefas para a camada J-1
Collaborator
Camada J-1
Exemplo concreto de uso do padrão:
FTP em cima de TCP em cima de IP em cima de
Ethernet em cima de cabo de par trançado.
Padrão Arquitetural para Sistemas Interativos: Model-View-Controller
Um dos primeiros padões identificados; surgiu na comunidade
de Smalltalk
Exemplo: eleição proporcional para deputado federal (desenhar
figura)
modelo: resultado parcial da apuração dos votos por
partido até o momento
visões: gráfico de torta, gráfico de barras,
divisão do parlamento (por partidos ou por blocos ou por oposição/situação),
tabela c/ número de deputados, tabela c/ número de votos
controlador: permite mudar, por exemplo, o número de votos
na tabela c/ o número de votos
Contexto: aplicações interativas que requerem interfaces
homem-computador flexíveis
Problema:
interfaces c/ o usuário são particularmente sensíveis
a mudanças (o cliente pede com freqüência e se o sistema
não comporta as mudanças podemos ter muita dor de cabeça).
a aplicação pode ter que ser implementada em outra
plataforma ou ter que utilizar outra aparência (look and feel
)
a mesma aplicação possui diferentes requisitos dependendo
do usuário.
um digitador prefere uma interface onde tudo pode ser feito através
do teclado e visualizado como texto
um gerente prefere uma interface através do mouse e de menus
com visualização gráfica
neste contexto, se o código para a interface gráfica
é muito acoplado ao código da aplicação, o desenvolvimento
pode se tornar muito caro e difícil
Solução: MVC
proposta inicialmente no Smalltalk-80, divide a aplicação
interativa em três partes:
processamento
saída
entrada
O modelo encapsula os dados e a funcionalidade principais da aplicação.
A saída é feita através das visões.
A entrada é feita através dos controladores
Estrutura:
Diagrama de classes englobando Model, View,
Controller e Observer
FIGURA
Dinâmica:
Cenário 1: mostra como a entrada de dados do usuário
altera o modelo e as visões
Cenário 2: mostra como a aplicação é
inicializada
Implementação:
em C++, uma superclasse Observer para definir a interface
de atualização é uma boa.
em Smalltalk isso não é necessário pois a classe
raiz Object já define isso.
Variação: Document-View
é uma degeneração do padrão onde cada
par (visão, controlador) é unido em um único componente,
a visão.
o modelo é chamado de documento.
Usos Conhecidos
Smalltalk
MFC da Microsoft do Visual C++ usa o padrão Document-View
ET++
Gaia: sistema operacional para computação ubíqua
Conseqüências:
Benefícios:
Desacopla as visões do modelo permitindo que múltiplas
visões sejam acrescentadas e removidas em tempo de execução
Permite a sincronização das múltiplas visões.
Permite a mudança de aparência (look and feel
) da aplicação com pouco esforço
Pode-se implementar um arcabouço implementado este padrão
e daí o mesmo código é reaproveitado par várias
aplicações
Malefícios:
Aumento na complexidade do código da aplicação
Pode ocorrer um número excessivo de atualizações
e, portanto, carga no sistema se tivermos muitas visões. Se o padrão
não é implementado com cuidado podemos ter coisas como envio
de atualizações para visões que estão minimizadas
ou fora do campo de visão do usuário (por estarem sobrepostas
por outras janelas.
Dificuldade de desacoplar visão e controlador e re-utilização
independente
Tanto as visões quanto os controladores chamam o modelo diretamente.
Mudanças na interface do modelo podem quebrar todas as visões
e controladores. Isso pode ser solucionado usando o padrão POSA Command
Processor (277).
Ineficiência: dependendo da interface do modelo, uma visão
pode ter que fazer inúmeras chamadas ao modelo para mostrar os dados.
Alguns ambientes para construção de interfaces já
misturam a visão como controlador (p.ex. pop-down-menu) dificultando
a utilização do MVC.
Padrão Arquitetural para Sistemas Adaptáveis: Microkernel
O uso de micronúcleos se aplica a sistemas que precisam ser adaptados
para mudanças nos requisitos.
Premissa básica do bom programador OO: Design for Change.
Exemplo:
Sistema operacional Hydra no qual podemos executar aplicações
Windows, UNIX System V ou NeXTSTEP simultaneamente em diferentes janelas.
Contexto:
Desenvolvimento de várias aplicações que utilizam
interfaces programáticas similares baseadas numa mesma funcionalidade.
Problema:
Software básico é utilizado ao longo de muitos anos,
às vezes décadas. Ao longo da sua vida, acontecem muitas mudanças
no hardware, nos modelos de programação, nos tipos de bibliotecas
utilizadas, e nos requisitos das aplicações.
O núcleo da funcionalidade do software básico deve ser
encapsulado em uma componente que seja o menor possível e que consuma
o mínimo possível de recursos computacionais para não
prejudicar as aplicações para as quais ele oferece suporte.
Solução:
Encapsule os serviçoes fundamentais da sua plataforma em uma
componente chamada "micronúcleo".
O micronúcleo oferece os seus serviços básicos
através de sua interface bem definida.
Funcionalidade que não é essencial e que não
pode ser implementada sem aumentar a complexidade do micronúcleo deve
ser implementada em "servidores internos" que estendem a funcionalidade do
micronúcleo.
Outras componentes chamdas de "servidores externos" utilizam a interface
do micronúcleo para prover outros tipos de interfaces baseadas na
funcionalidade exportada pelo micronúcleo.
Os clientes (aplicações) interagem com o sistema através
dos servidores externos.
Estrutura:
Diagrama de classes
Usos conhecidos:
Sistemas operacionais baseados em micronúcleos:
Mach (CMU, 1986), hoje base do MacOS X
Amoeba (Tanenbaum, 92), OS orientado a objetos
Windows NT (Microsoft, 93), oferecia 3 servidores externos: OS/2,
POSIX e Win32.
Banco de Dados basedo em micronúcleo:
MKDE (Microkernel DatenBank Engine) de 1996. Diferentes tipos de
bancos de dados com interfaces diferentes são implementados em cima
de um microkernel básico.
Middleware de Comunicação:
UIC - Universally Interoperable Core (Román, 2000). O micronúcleo
contém apenas
um arcabouço abstrato para construção de
middleware para objetos distribuídos
um mecanismo para carga dinâmica de componentes para integrar
o arcabouço
Isso é suficiente para carregar incrementalmente em tempo
de execução um middleware para comunicação e m
sistemas distribuídos, por exemplo, um subconjunto de CORBA.
Padrão Arquitetural para Sistemas Adaptáveis: Reflection
Padrão baseado na idéia de Reflexão Computacional
proposta na tese de doutorado de Brian Smith em 82 no MIT.
Exemplo:
Considere um sistema em C++ que necessita que seus objetos sejam guardados
no disco de forma persistente.
Poderíamos iplementar um método para ler e escrever
objetos do disco para cada tipo de objeto: obviamente ruim.
Poderíamos oferecer uma superclasse básica para objetos
persistentes da qual todos os objetos persistentes deveriam herdar. As subclasses
deveriam sobrecarregar os métodos para leitura e escrita do disco.
Problema: mudanças nas classes nos forçariam a atualizar estes
métodos manualmente, ou seja, persistência e a funcionalidade
básica da aplicação estão fortemente acopladas/misturadas.
Queremos desenvolver um mecanismo de persistência desacoplado
da funcionalidade das aplicações.
Este mecanismo deveria ser capaz de enxergar em tempo de execução
a estrutura interna dos objetos e decidir o que deve ser armazenado em disco.
Contexto:
Construção de sistemas que permitem, a priori, a sua
própria modificação.
Problema:
Os requisitos para uma dada aplicação evoluem ao longo
do tempo.
Forças:
Mudar software convencional (que não está preparado
para mudanças) é muito difícil e sujeito a erros.
Sistemas de software adaptáveis em geral tem uma estrutura
interna complexa
Quanto maior o número de mecanimos diferentes que aplicamos
em um sistema para torná-lo mais adaptável, mais complexo ele
fica.
As mudanças exigidas podem ser de muitos tipos; desde adicionar
um pequeno atalho a uma seqüência de comandos até mudar
completamente uma aplicação para adaptá-la a um cliente
específico.
Solução:
Faça com que
o software seja ciente de si mesmo (self-aware), que ele
conheça a sua própria estrutura e
que seja possível inspecionar e modificar a sua estrutura
interna e comportamento.
Isso leva a uma arquitetura separada em dois níveis: nível
base e meta-nível.
O meta-nível oferece uma auto-representação
do software para que ele tenha conhecimento da sua própria estrutura
e comportamento. Ele é composto de meta-objetos.
Os meta-objetos guardam informações sobre os
tipos utilizados, os algoritmos utilizados, os mecanismos de comunicação
utilizados, os mecanismos de persistência utilizados, etc.
O nível base define a lógica da aplicação
e é composto pelos objetos da aplicação. Os objetos utilizam
os meta-objetos para os aspectos não-funcionais.
Por exemplo, para fazer uma chamada a um método de outro
objeto, um objeto usa o meta-objeto de comunicação. Mudando-se
o meta-objeto de comunicação pode-se mudar o mecanismo de comunicação
entre objetos.
Para manipular os meta-objetos usa-se uma interface chamada de
Protocolo dos Meta-Objetos, ou Metaobject Protocol (
MOP).
Usando-se o MOP pode-se, por exemplo, criar um novo objeto para
ser o meta-objeto de comunicação e, assim, mudar o mecanismo
de comunicação utilizado.
Outro Exemplo: pode-se implementar um componente responsável
por persistência de objetos no nível base. Este componente utilizaria
um meta-objeto responsável por informar os detalhes da estrutura de
dados de um objeto do nível básico que deve ser persistido.
O componente de persistência vê quais são os dados a persistir
e os guarda no disco.
Estrutura:
Classe
Objeto do Nível Base
Responsabilidade
Implementa a lógica da aplicação
Pode usar informações oferecidas pelo
meta-nível
Colaborador
Meta-Nível
Classe
Objeto do Meta-Nível
Responsabilidade
Encapsula um dos aspectos internos do sistema que
podem mudar
Provê uma interface que permite modificações
em um aspecto do sistema
Colaborador
Nível Base
Classe
MOP
Responsabilidade
Oferece uma interface para efetuar mudanças
no meta-nível
Efetua as modificações solicitadas
Colaborador
Meta-Nível
Nível Base
Usos Conhecidos:
CLOS: uma linguagem de programação reflexiva (extensão
de LISP) de 1989.
MIP: um sistema para inspeção de tipos C++ em tempo
de execução de 1992.
PGen: um sistema para persistência de objetos C++ baseado em
MIP; de 1994.
NEDIS: um sistema para venda de carros que usa reflexão para
permitir adaptações para diferentes tipos de concessionárias
e informações específicas para cada país (p.ex.,
moeda, língua, cálculo de impostos).
Apertos:
um sistema operacional reflexivo que usa meta-objetos para
controlar as políticas e mecanismos para gerenciamento de memória,
escalonamento, etc. (Sony Labs, 1992)
dynamicTAO: um ORB CORBA que usa reflexão para controlar
aspectos de segurança, monitoração e configuração
automática de aplicações CORBA (1999).
Guaraná: uma extensão de Java para permitir reflexão
completa (1999). A "reflexão" de Java permite apenas introspecção,
i.e., pode-se olhar a estrutura do sistema mas não é possível
mexer em nada.
Referência
F. Buschmann, R. Meunier, H. Rohnert, P.Sommerlad, M. Stal
Pattern-Oriented Software Architecture - A System of
Patterns. John Wiley and Sons Ltd, Chichester, UK, 1996