MAC 413/5715 - Tópicos de Programação Orientada a Objetos
Aula 3 - 15/8/4
POSA I - 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.
Um sistema reflexivo é um sistema que possui uma representação de si
mesmo e é capaz de refletir sobre ela e manipular a si mesmo através da
manipulação desta representação
O conceito foi posteriormente desenvolvido por Gregor Kiczales
introduzindo um modelo OO para reflexão com a linguagem CLOS
descrita no livro The Art of the Metaobject Protocol.
Exemplo:
Considere um sistema em C++ que necessita que seus objetos
sejam guardados
no disco de forma persistente.
Poderíamos implementar 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.
JBoss: um servidor de aplicações.
Smalltalk, Python e Lua (em geral, linguagens dinâmicas
costumam ser reflexivas).
A aplicação deste padrão traz uma série de conseqüências, tanto
positivas quanto negativas (ver pag. 217).
POSA I - Expressões Idiomáticas (Idioms)
Expressões idiomáticas ou idioms são padrões de
bem baixo nível
específicos para um determinada linguagem de
programação
explicam como implementar um determinado conceito utilizando os
mecanismos de uma linguagem específica
Nota lingüística: idiom
não se traduz para "idioma", é um falso cognato. "idioma"
em português é language
em inglês.
À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++ do POSA I, pag. 351
incluir aqui singleton em Smalltalk do POSA I, pag. 351
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, pag. 355
ver código fonte da implementação no
POSA,
pag. 356
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 (para esconder sujeira não OO em
baixo de um tapete OO)
Component Configurator (para carga e descarga dinâmica de
componentes)
Interceptor (para interceptar e modificar chamadas a
métodos)
Extension Interface (permite que uma componente exporte
múltiplas interfaces)
Reactor (para receber eventos e reagir a eles demultiplexando e
os repassando aos objetos correspondentes)
Acceptor-Connector (para estabelecer conexões entre dois
pontos da rede (desacopla a conexão do processamento))
Leader-Follower (padrão do ponto de taxi)
Scoped Locking (expressão C++ para bloquear objetos
baseados no escopo de execução)
Thread-Specific Storage
Referências
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