MAC 413/5715 - Tópicos de Programação
Orientada a
Objetos
Aula 2 - 27/08/2004
Padrões Arquiteturais (POSA I, II e III)
Pattern-Oriented Software Architecture
POSA I: A System of Patterns
POSA II: Patterns for Networked and Concurrent Objects
POSA III: Patterns for
Resource Management
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 de Projeto: Publisher-Subscriber
É na verdade o Observer (293) do GoF.
O POSA o inclui para descrever variantes interessantes do
padrão
Problema:
em alguns sistemas, os dados mudam em um certo lugar e
vários objetos em outras partes do sistema dependem daqueles
dados; é necessário então, utilizar algum
mecanismo para informar os dependentes das mudanças.
poder-se-ia introduzir chamadas explícitas do objeto
cujo estado mudou para os seus dependentes, mas esta
solução não é flexível nem
reutilizável.
precisamos de um mecanismo de propagação de
mudanças que possa ser aplicado em vários contextos
A solução tem que equilibrar as seguintes forças:
um ou mais objetos tem que ser notificados sobre
mudanças em um objeto
o número de dependentes não é conhecido
a priori e pode até mudar dinamicamente
fazer com que os dependentes peguem o estado de tempos em
tempos (polling) é
muito caro
o objeto que provê o dado e aqueles que o consomem
não devem ser fortemente acoplados
Solução:
O objeto que contém o dado que interessa é
chamado de Publisher (subject no GoF)
Os objetos dependentes são chamados de Subscribers (observers no GoF)
O publisher mantém uma lista dos objetos registrados que
são aqueles interessados em receber notificações
sobre as mudanças.
O publisher oferece uma interface para
manifestação de interesse (subscribe interface)
Quando o estado do publisher muda, ele envia uma
notificação para todos os objetos que manifestaram
interesse.
Ao receber uma notificação, o subscriber decide
se solicita a informação ao publisher ou não.
Há vários graus de liberdade ao implementar o
padrão:
pode-se utilizar classes abstratas para implementar o
comportamento básico do padrão
o publisher pode decidir quais partes do seu estado interno
serão notificadas
o publisher pode decidir enfileirar as mudanças de
estado antes de enviar as notificações
um objeto pode se inscrever em vários publishers
um objeto pode ser simultaneamente publisher e subscriber
inscrição e notificação pode ser
filtrada de acordo com o tipo de evento gerado, ou tipo de dado que foi
alterado
o publisher pode enviar junto com a notificação
informações sobre o que mudou, ou não
Dois extremos:
modelo push (pouca
flexibilidade mas possivelmente melhor uso da banda)
modelo pull (mais
flexibilidade mas maior número de mensagens)
Variações
Gatekeeper
Versão distribuída do padrão
um publisher em um processo notifica subscribers remotos
o publisher pode ser dividido em dois processos
um processo gera os eventos
outro processo faz a demultiplexação definindo
quais subscribers o receberão e enviando as mensagens
Event channel
Proposto pela OMG no CORBA Event Service
desacopla ainda mais os produtores dos consumidores incluindo
um canal (channel) ligando os
dois
neste caso, os consumidores não precisam saber a origem
dos eventos, não interessa quem é o publisher, o que
interessa é o que foi publicado
um ou mais canais são introduzidos no meio de um ou mais
publishers e um ou mais subscribers
para os publishers, o event channel se parece com um subscriber
para os subscribers, o event channel se parece com um publisher
publishers, channels e subscribers podem residir em
máquinas distintas
channels podem oferecer serviços adicionais:
bufferização, filtragem, priorização
Producer-consumer
dois threads em um processo ou dois processos em uma
máquina que compartilham um buffer
um deles produz dados e coloca no buffer e o outro os retira do
buffer e os consome
não há comunicação direta entre
eles, mas há controle do acesso concorrente ao buffer
compartilhado
há uma relação de 1:1 que não
existe nas outras variantes do padrão
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 hipotético 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 em
sistemas distribuídos, por exemplo, um subconjunto de CORBA.
Servidor de Aplicações:
JBoss
Plataforma para criação de
aplicações locais
Eclipse
Expressões Idiomáticas (idioms)
são padrões de bem baixo nível
específicos para um determinada linguagem de
programação
explica como implementar um determinado conceito utilizando os
mecanismos de uma linguagem
Às vezes ao olharmos para um padrão de projeto em
uma determinada linguagem, podemos ser levados a uma expressão
idiomática. Por exemplo, o padrão Singleton em C++ e
Smalltalk podem ser vistos como idioms
incluir aqui singleton em C++
incluir aqui singleton em Smalltalk
Os livros Effective C++
and More Effective C++ de
Meyers e o livro Effective Java
são bons exemplos de livros de expressões
idiomáticas.
O POSA apresenta apenas um exemplo de expressão
idiomática mais elaborada:
Counted Pointer
Problema:
alocação dinâmica de objetos em C++ causa
muitos problemas de vazamento de memória
objetos tem que ser destruídos explicitamente e muitas
vezes não sabemos o lugar apropriado para destruí-lo (e
liberar sua memória
em C++ usamos muito passagem de objetos como parâmetro,
mas quando fazemos isso, quem deve ser responsável por destruir
o objeto? O chamador ou o chamado?
forças:
passar objetos sempre por valor não é
apropriado
vários clientes precisam compartilhar um mesmo objeto
não podemos permitir referências para objetos
que já foram destruídos (dangling
references)
se um objeto não é mais utilizado, ele deve
ser destruído para liberar os recursos
a solução não deve exigir muito
código adicional e deve ser leve computacionalmente
Solução
introduza contagem de referências utilizando um
"apontador contador" ao invés de apontadores simples
adicione um contador de referências na classe original
crie uma classe Handle que servirá de "apontador
inteligente" para o objeto original
ver diagrama UML no POSA
ver código fonte da implementação no POSA
Padrões interessantes no POSA II
No POSA II, Patterns for Networked and Concurrent Objects, sugiro em
especial, os seguintes padrões que são bem interessantes e úteis:
Wraper Façade
Component Configurator
Interceptor
Extension Interface
Reactor
Acceptor-Connector
Leader-Follower
(padrão do ponto de taxi)
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