Sérgio Teixeira de Carvalho
D031 −−−− Março de 2001UM DESIGN PATTERN PARA A CONFIGURAÇÃO DE
ARQUITETURAS DE SOFTWARE
Dissertação apresentada ao Curso de
Pós-Graduação em Computação Aplicada e
Automação da Universidade Federal
Fluminense, como requisito para a
obtenção do Grau de Mestre. Área de
Concentração: Computação Distribuída e
Paralela.
Orientador: Prof. ORLANDO GOMES LOQUES FILHO.
Niterói
2001
ii
2001 Um Design Pattern para a Configuração de Arquiteturas de Software/ Sérgio Teixeira de Carvalho.– Niterói: UFF/CTC, 2001.
180 f.
Dissertação (Mestrado)
–
Computação Aplicada e Automação, Universidade Federal Fluminense, 2001.iii
UM DESIGN PATTERN PARA A CONFIGURAÇÃO DE
ARQUITETURAS DE SOFTWARE
Dissertação apresentada ao Curso de Pós-Graduação em Computação Aplicada e Automação da Universidade Federal Fluminense, como requisito para a obtenção do Grau de Mestre. Área de Concentração: Computação Distribuída e Paralela. Aprovada em março de 2001.
BANCA EXAMINADORA
_____________________________________________________ Prof. Orlando Gomes Loques Filho, PhD – Orientador
Universidade Federal Fluminense
_____________________________________________________ Prof. Julio Cesar Sampaio do Prado Leite, PhD.
Pontifícia Universidade Católica do Rio de Janeiro
_____________________________________________________ Prof. Julius Cesar Barreto Leite, PhD.
Universidade Federal Fluminense
_____________________________________________________ Prof. Maria Luiza d’Almeida Sanchez, DSc.
Universidade Federal Fluminense Niterói
iv LISTA DE ABREVIATURAS ... x RESUMO ... xi ABSTRACT ... xiii 1. INTRODUÇÃO ... 1 1.1 OBJETIVOS ... 3 1.2 ORGANIZAÇÃO ... 5
2. PATTERNS E ARQUITETURAS DE SOFTWARE ... 6
2.1 ARQUITETURAS DE SOFTWARE ... 7
2.1.1 Elementos de uma Arquitetura ... 9
2.1.1.1 Componentes ... 9 2.1.1.2 Conectores ... 11 2.1.2 Descrição Arquitetural ... 14 2.1.3 Implementação ... 16 2.2 PATTERNS ... 17 2.2.1 Definição e Fundamentos ... 19 2.2.2 Tipos de Patterns ... 22 2.2.2.1 Architectural Patterns ... 22 2.2.2.2 Design Patterns ... 23 2.2.2.3 Idiomas ... 25 2.2.3 Descrição de Patterns ... 26
2.3 PATTERNS EM ARQUITETURAS DE SOFTWARE ... 30
2.3.1 Estilos Arquiteturais ... 32
2.3.2 Frameworks ... 32
2.3.3 Linguagem de Pattern (Pattern Language) ... 33
2.4 CONCLUSÃO ... 36
v
3.3 ELEMENTOS ... 44
3.3.1 Portas ... 44
3.3.2 Componentes ... 46
3.3.3 Conectores ... 47
3.3.3.1 Manipulação das Requisições e das Respostas ... 50
3.3.3.2 Encadeamento ... 52 3.3.3.3 Interceptação ... 55 3.3.4 Configuração ... 58 3.4 DIAGRAMA DE CLASSES ... 61 3.5 ASPECTOS ... 61 3.6 CONCLUSÃO ... 64
4. DESIGN PATTERN ARCHITECTURE CONFIGURATOR ... 66
4.1 DESCRIÇÃO ... 67 Nome ... 67 Contexto ... 67 Exemplo ... 67 Problema ... 71 Forças ... 71 Solução ... 72 Estrutura e Participantes ... 76 Colaborações ... 79 Implementação ... 85 Conseqüências ... 96 Patterns Relacionados ... 97 Usos Conhecidos ... 100 Código-Exemplo ... 102 4.1 CONCLUSÃO ... 107 5. EXEMPLOS ... 108 5.1 METODOLOGIA ... 109 5.2 COMUNICAÇÃO DISTRIBUÍDA ... 111
5.2.1 Aplicação com CORBA ... 111
5.2.1.1 Implementação ... 112
5.2.1.2 Modificando a Arquitetura ... 116
5.2.2 Aplicação com Sockets ... 121
5.2.2.1 Implementação ... 123
5.2.2.2 Modificando a Arquitetura ... 129
5.3 SINCRONIZAÇÃO E CONCORRÊNCIA ... 130
5.3.1 Aplicação Produtor-Consumidor com buffer limitado ... 130
5.3.1.1 Implementação ... 132
5.3.1.2 Modificando a Arquitetura ... 135
5.4 PUBLISHER-SUBSCRIBER ... 136
vi
...
6. CONCLUSÃO ...148
6.1 CONTRIBUIÇÃO ... 148
6.1.1 O Architecture Configurator em uma Linguagem de Patterns ... 151
6.2 TRABALHOS FUTUROS ... 153
APÊNDICE A. NOTAÇÕES ... 156
A.1 CARTÃO CRC ... 156
A.2 DIAGRAMA DE CLASSE UML ...157
A.3 DIAGRAMA DE SEQÜÊNCIA UML ...158
vii
FIGURA 2-2 ARQUITETURA DE UM SISTEMA CLIENTE-SERVIDOR SIMPLES ... 13
FIGURA 2-3 RELACIONAMENTO ENTRE ELEMENTOS DA DESCRIÇÃO DE UM PATTERN ... 27
FIGURA 3-1 DIAGRAMA DE CLASSES SIMPLIFICADO DO DESIGN PATTERN ARCHITECTURE CONFIGURATOR ... 42
FIGURA 3-2 REPRESENTAÇÃO DE UM COMPONENTE CLIENTE SOLICITANDO SERVIÇO A UM COMPONENTE SERVIDOR ... 47
FIGURA 3-3 CLASSE CONECTOR ... 48
FIGURA 3-4 CONECTOR E AS OPERAÇÕES HANDLE() E FORWARD() ... 49
FIGURA 3-5 CONFIGURAÇÃO COM DOIS CONECTORES ENCADEADOS ... 54
FIGURA 3-6 MODELO COM DOIS CONECTORES CONFIGURADOS ... 55
FIGURA 3-7 INTERFACE SERVIDOR INTERCEPTA SOLICITAÇÕES DE CLIENTE E REPASSA INFORMAÇÕES PARA HANDLER ... 56
FIGURA 3-8 CONFIGURAÇÃO, CONFIGURADOR E PORTA ... 59
FIGURA 3-9 DIAGRAMA DE CLASSES COMPLETO DO DESIGN PATTERN ARCHITECTURE CONFIGURATOR ... 62
FIGURA 3-10 CORRESPONDÊNCIA ENTRE AS ABSTRAÇÕES PROGRAMAÇÃO DA CONFIGURAÇÃO, PROGRAMAÇÃO DO META-NÍVEL E O DESIGN PATTERN ARCHITECTURE CONFIGURATOR ... 64
FIGURA 4-1 ARQUITETURA DA APLICAÇÃO PRODUTOR-CONSUMIDOR COM BUFFER LIMITADO ... 68
viii
FIGURA 4-4 CARTÕES CRC DAS CLASSES HANDLER, SERVIDOR HANDLER, CONFIGURAÇÃO E
PORTA ... 78
FIGURA 4-5 DIAGRAMA DE CLASSES DO DESIGN PATTERN ARCHITECTURE CONFIGURATOR ... 80
FIGURA 4-6 UM POSSÍVEL DIAGRAMA DE OBJETOS DO ARCHITECTURE CONFIGURATOR ... 80
FIGURA 4-7 DIAGRAMA DE SEQÜÊNCIA (COLABORAÇÃO). PRIMEIRA FASE ... 82
FIGURA 4-8 DIAGRAMA DE SEQÜÊNCIA (COLABORAÇÃO). SEGUNDA FASE ... 83
FIGURA 4-9 CLASSES CONFIGURAÇÃO E CONFIGURADOR, DE ARCHITECTURE CONFIGURATOR, ASSOCIADAS AO PATTERN COMPONENT CONFIGURATOR, DESCRITO EM [SSR00] ... 99
FIGURA 5-1 EXEMPLO DE APLICAÇÃO CLIENTE-SERVIDOR VIA CORBA ...112
FIGURA 5-2 IMPLEMENTAÇÃO DO CONECTOR CORBAC ...114
FIGURA 5-3 DIAGRAMA DE OBJETOS E DE DISTRIBUIÇÃO DA APLICAÇÃO CLIENTE-SERVIDOR VIA CORBA ...115
FIGURA 5-4 ARQUITETURA CLIENTE-SERVIDOR VIA CORBA MODIFICADA ... 117
FIGURA 5-5 DIAGRAMA DE OBJETOS E DE DISTRIBUIÇÃO CLIENTE-SERVIDOR VIA CORBA MODIFICADO ...121
FIGURA 5-6 EXEMPLO DE APLICAÇÃO CLIENTE-SERVIDOR COM COMPONENTES DISTRIBUÍDOS COMUNICANDO-SE VIA SOCKETS ...123
FIGURA 5-7 MODELO DO SOCKETC SERVIDOR ... 126
FIGURA 5-8 ARQUITETURA DA APLICAÇÃO PRODUTOR-CONSUMIDOR COM BUFFER LIMITADO .... 131
FIGURA 5-9 ARQUITETURA DA APLICAÇÃO PRODUTOR-CONSUMIDOR MODIFICADA ... 135
FIGURA 5-10 ARQUITETURA DA APLICAÇÃO JOGO DA VELHA COM DESIGN PATTERN PUBLISHER -SUBSCRIBER ... 137
FIGURA 6-1 PARTE DO DIAGRAMA DA LINGUAGEM DE PATTERN DO LIVRO POSA2 ACRESCIDA DO ARCHITECTURE CONFIGURATOR ... 151
ix
CÓDIGO 3-1 OPERAÇÃO HANDLE()... 51
CÓDIGO 3-2 OPERAÇÃO FORWARD()... 53
CÓDIGO 3-3 OPERAÇÃO SERVICO1() IMPLEMENTADA EM INTERFACE SERVIDOR ... 57
CÓDIGO 4-1 DESCRIÇÃO DA ARQUITETURA DA APLICAÇÃO PRODUTOR-CONSUMIDOR COM BUFFER LIMITADO (PCB) ... 70
CÓDIGO 5-1 CONECTOR CORBAC CLIENTE (CLASSE CORBAC_CLT) ... 118
CÓDIGO 5-2 CONECTOR CORBAC SERVIDOR (CLASSE CORBAC_SRV) ... 119
CÓDIGO 5-3 CONECTOR SOCKETC CLIENTE (CLASSE SOCKETC_CLT) ...124
CÓDIGO 5-4 CLASSE SOCKETTCP ...127
CÓDIGO 5-5 CLASSE SOCKETUDP ... 128
CÓDIGO 5-6 CONECTOR GUARDA ... 133
CÓDIGO 5-7 CONECTOR PUBLISHER SUBSCRIBER (CLASSE PUBSUBC) ... 140
CÓDIGO 5-8 CONECTOR MULTICAST CLIENTE ... 143
CÓDIGO 5-9 CONECTOR MULTICAST SERVIDOR ... 144
x
CRC CLASS-RESPONSIBILITY-COLLABORATOR
GoF GANG OF FOUR
IDL INTERFACE DEFINITION LANGUAGE
IP INTERNET PROTOCOL
MVC MODEL-VIEW-CONTROLLER
PCB PRODUTOR-CONSUMIDOR-BUFFER
PLOPD PATTERN LANGUAGES OF PROGRAM DESIGN
POSA PATTERN-ORIENTED SOFTWARE ARCHITECTURE
QoS QUALITY OF SERVICE
R-RIO REFLECTIVE-RECONFIGURABLE INTERCONNECTABLE OBJECTS
TCP TRANSMISSION CONTROL PROTOCOL
UDP USER DATAGRAM PROTOCOL
xi
conceitos de arquiteturas de software e patterns. Enfocamos a descrição de uma arquitetura e a organização de seus elementos básicos, bem como as categorias de
patterns e as formas de descrevê-los. Abordamos ainda a possível integração de patterns em arquiteturas de software.
Em seguida, apresentamos uma proposta de design pattern para organizar, no nível de implementação, componentes e conectores incluídos na configuração de uma arquitetura de software. O design pattern proposto, o qual denominamos Architecture Configurator, fornece uma base para a implementação de configurações arquiteturais, fundamentada nos mecanismos de interceptação, encaminhamento e manipulação de requisições, bem como na interligação dos componentes e conectores. Esses mecanismos são aplicados de forma transparente em relação aos elementos básicos da arquitetura, suportando propriedades como reutilização, abstração e separação de interesses. O design pattern permite que componentes e conectores participantes da arquitetura possam ser implementados de forma autônoma e integrados de acordo com a configuração arquitetural.
Na apresentação da proposta, associamos os elementos que compõem uma arquitetura de software aos elementos definidos no Architecture Configurator. Em
xii
problema, solução, diagramas UML, além de características relacionadas à sua implementação.
Finalmente, descrevemos exemplos de aplicações, cujas arquiteturas foram implementadas tendo como base o design pattern Architecture Configurator. As aplicações foram desenvolvidas utilizando-se a linguagem de programação Java.
xiii
patterns. We deal with the description of architectures and the organization of their basic elements, including the concept of patterns and the mechanisms to describe them. In addition, we consider the possibility of integrating patterns into software architectures.
Further on, we present a proposal of a design pattern to organize, in the implementation level, the components and connectors included in the configuration of a software architecture. The proposed design pattern, which we call the Architecture
Configurator, supplies a basis for the implementation of architectural configurations,
using the mechanisms of interception, guiding and manipulation of requests, as well as supporting the interconnection of components and connectors. These mechanisms are applied in a transparent fashion to the basic elements of a software architecture, supporting properties such as reuse, abstraction and separation of concerns. The proposed design pattern allows the components and connectors to be implemented separately and to be integrated into an architectural configuration at a latter stage.
In the proposal, we associate the elements that compose a software architecture and the elements defined in the Architecture Configurator. After that, we
xiv
diagrams UML, as well as the details related to its implementation.
Finally, we describe examples of applications, whose architectures were implemented using the design pattern Architecture Configurator.
1
evolução de sistemas de software. Um sistema de software pode ser desenvolvido a partir da descrição de sua arquitetura, a qual define, dentre outros elementos, componentes e conectores.
A forma de interação entre os elementos de uma arquitetura, bem como o projeto e/ou estrutura empregados em cada um deles, podem ser reaproveitados através do uso de patterns. Um pattern descreve determinado problema, recorrente no desenvolvimento de software, e sua respectiva solução. Isso permite ao desenvolvedor a reutilização de soluções, seja no contexto da arquitetura, projeto ou implementação do sistema. Os próprios desenvolvedores definem os patterns para que outros possam utilizá-los nos seus respectivos contextos de desenvolvimento de software.
Esta dissertação tem seu foco na investigação do emprego de patterns na organização dos elementos de uma arquitetura de software. Especificamente, apresentamos a proposta de um design pattern destinado a organizar, no nível de implementação, componentes e conectores definidos quando da configuração de uma arquitetura.
A programação da configuração, realizada durante o processo de descrição da arquitetura de software, estabelece a estrutura topológica da mesma através da interligação dos componentes e conectores. Componentes, em geral, possuem uma interface através da qual podem fornecer serviços e requerer serviços. Os conectores, fundamentais na arquitetura, intermediam a interação entre componentes, interceptando, manipulando e encaminhando as requisições e respostas.
Para elaboração deste trabalho, realizamos um estudo relacionado à integração de patterns em arquiteturas de software. Implementamos alguns design
patterns em conectores (Chain of Responsibility, Visitor, Memento, Proxy e State
disponíveis em [GHJ95]), utilizando o ambiente de suporte R-RIO [LLS99, Lob99], o qual permite a concepção e evolução de sistemas baseados em arquiteturas de software. Em [Car99] apresentamos esse estudo, destacando os design patterns considerados e os exemplos de implementação utilizando conectores R-RIO.
Observamos, no decorrer da investigação, um certo padrão quanto à implementação de conectores. Mecanismos de interceptação, manipulação e encaminhamento de requisições apresentavam-se de forma recorrente. A princípio pareceu-nos que um pattern baseado nestes mecanismos poderia ser descrito para auxiliar na construção de conectores. Entretanto, tal pattern não teria a abordagem satisfatória dentro do contexto de arquiteturas de software, uma vez que ele teria sua
descrição associada apenas a conectores, não incluindo o inter-relacionamento entre componentes e conectores, e a própria programação da configuração.
Diante desse aspecto, analisamos o inter-relacionamento de conectores e componentes, estabelecido de acordo com a programação da configuração do sistema de software. Basicamente, (i) componentes e conectores possuem portas, às quais identificam seus pontos de interação; (ii) as portas utilizadas na comunicação entre dois componentes devem ter o mesmo tipo; (iii) um componente interage com outro componente por intermédio de um ou mais conectores; e (iv) um conector pode ser conectado diretamente a outro conector. Observamos que a interação entre conectores e componentes apresenta um padrão básico quanto à sua organização.
Constatamos desse modo que os mecanismos de implementação utilizados, tanto nos conectores quanto na programação da configuração, apresentam-se de forma recorrente, podendo ser descritos em um design pattern.
O design pattern Architecture Configurator, apresentado nesta dissertação, concentra-se em quatro princípios básicos: (i) encadeamento de conectores e componentes; (ii) interceptação de requisições; (iii) manipulação de requisições; e (iv) encaminhamento de requisições. A solução descrita pelo design pattern serve de base à implementação de configurações arquiteturais. Implementação, nesse contexto, refere-se à organização dos elementos da arquitetura no nível de implementação, conforme a configuração programada para a mesma.
1.1 Objetivos
- apresentar uma revisão das áreas de arquiteturas de software e de
patterns;
- apresentar a proposta do design pattern Architecture Configurator, associando seus elementos estruturais com os elementos básicos de arquiteturas de software;
- descrever, a partir do formato básico Problema-Contexto-Solução, a estrutura do design pattern proposto, seus elementos participantes, colaborações entre eles, outros patterns relacionados, código-exemplo de implementação, etc;
- validar a proposta com exemplos de aplicações desenvolvidas em linguagem Java [AGo97] e totalmente baseadas no design pattern
Architecture Configurator.
O formato adotado na descrição do design pattern foi obtido a partir de pesquisas junto à bibliografia relacionada, incluindo as seguintes publicações: GoF1 [GHJ95], POSA1 [BMR96], POSA2 [SSR00], PLoPD3 [MRB97], PLoPD4 [HFR99].
Para a validação, foram desenvolvidos exemplos de comunicação distribuída, baseados em CORBA (Common Object Request Broker Architecture) [OMG98] e sockets; sincronização e concorrência; e o jogo tic-tac-toe (jogo da velha), baseado no pattern Publisher-Subscriber [BMR96] de interação entre componentes.
1.2 Organização
A dissertação está organizada em seis capítulos e um apêndice.
O capítulo 2 aborda conceitos básicos de arquitetura de software e
patterns, apresentando uma revisão das respectivas áreas. Em relação à arquitetura, o
capítulo apresenta seus elementos e o conceito de descrição arquitetural, dentre outros tópicos. Os patterns são abordados quanto aos seus fundamentos, categorias e quanto ao formato de descrição.
A proposta é apresentada no capítulo 3. Nele é traçada uma associação entre os elementos participantes do design pattern proposto e os elementos que compõem uma arquitetura de software. O modelo de classes do design pattern é mostrado e aspectos relacionados à configuração são descritos.
No capítulo 4 é apresentada a descrição detalhada do design pattern
Architecture Configurator. Nesta descrição são apresentados, dentre outras seções, o
problema, a solução, diagramas de classe e seqüência UML (Unified Modeling
Language) [BRJ99], além de características relacionadas à sua implementação e
código-exemplo.
Exemplos de aplicações em linguagem Java, para a validação da proposta, são apresentados no capítulo 5. Exemplos relacionados à comunicação distribuída, concorrência/sincronização e interação entre componentes são descritos.
O capítulo 6 apresenta as conclusões e propostas para trabalhos futuros, e o apêndice A resume algumas das notações UML utilizadas na descrição do design
6
!
"
#$
A construção de um sistema de software parte da definição de uma arquitetura que possa descrevê-lo em termos de seus componentes e de seus conectores.
Durante o processo de concepção de um sistema de software, patterns podem ser utilizados com o objetivo de maximizar o reaproveitamento, desde a fase arquitetural do desenvolvimento até sua implementação. Patterns são meios de representar e registrar as soluções - de arquitetura, de projeto e de implementação - e a experiência do desenvolvedor ao aplicar estas soluções.
Os fundamentos de patterns e arquiteturas de software são apresentados neste capítulo. A primeira seção trata de arquiteturas, através da abordagem de
conceitos básicos, de seus elementos fundamentais (componentes, conectores, etc.) e, na seqüência, da descrição arquitetural através das ADLs (Architecture Description
Language). Os patterns são tratados em seguida, na segunda seção. Seus fundamentos,
categorias e formas de descrição são apresentados. A última seção aborda a integração de patterns e arquiteturas.
Essa abordagem conceitual de patterns e arquiteturas de software forma a base necessária à apresentação da proposta desta dissertação: um pattern que auxilie no processo de implementação da configuração de arquiteturas de software.
2.1 Arquiteturas de Software
Reutilização, modularidade, abstração e separação de interesses constituem-se em alguns conceitos há muito tempo investigados. Tais conceitos são entrelaçados e têm como fundamento a concepção de sistemas de software de qualidade, compostos de elementos reaproveitáveis em outros contextos e projetos.
Alguns desses conceitos, como reutilização e modularidade, remontam às linguagens de programação onde o código, escrito através de módulos (procedures,
functions, blocos, etc.) coesos e fracamente acoplados, pode ser reutilizado em outros
programas. Entretanto, um sistema de software não pode ser concebido partindo-se apenas da especificação de algoritmos ou estruturas de dados. É preciso que seja desenvolvido tendo-se sua organização como uma composição de componentes e interações entre estes componentes [SG96].
Um sistema de software pode ser concebido a partir da descrição de uma arquitetura do mesmo em termos dos componentes e suas interações [SDZ96]. Tal
arquitetura especifica a estrutura do sistema e sua topologia, mostrando a correspondência entre os requisitos do sistema e os seus elementos. Componentes e suas interações, às quais chamamos conectores, são definidos através de tipos abstratos (classes) e formam a estrutura da arquitetura. A topologia, por sua vez, é caracterizada pela configuração, através da qual instâncias de componentes e conectores são criadas e interligadas.
Além de componentes e conectores, a porta é outro elemento fundamental que compõe a arquitetura de um sistema de software. Portas são objetos tipados que identificam pontos de interação de um componente/conector e seu ambiente [LLS99]. Elas representam a interface de componentes e conectores e permitem que estes possam ser interligados para formar a topologia da arquitetura. Componentes, conectores e portas podem ser definidos e especificados de forma independente uns dos outros e posteriormente configurados em uma arquitetura, ou ainda reutilizados em diferentes contextos. Por exemplo, em um sistema cliente-servidor de três camadas, o servidor, o banco de dados, e o código que permite a comunicação entre eles (conector), podem ser construídos separadamente.
A pesquisa em arquitetura de software está direcionada à redução de custos no desenvolvimento de aplicações [DW92, SG96]. Ela fornece um entendimento amplo do sistema pois permite ao projetista concentrar-se nos elementos arquiteturais (componentes e conectores) e na sua estrutura de interconexões (configuração), deslocando-o do foco concentrado em linhas de código (programming in the large x
programming in the small). Isso proporciona ao projetista um alto nível de abstração,
fazendo-o concentrar-se na estrutura global em vez de em detalhes não tão relevantes do processo de desenvolvimento do sistema.
Outra propriedade de arquiteturas refere-se à separação de interesses: uma arquitetura de software auxilia na separação da computação de um sistema (realizada pelos componentes) da interação entre as unidades computacionais do sistema [Med99]. A partir da separação de interesses, funções básicas de uma aplicação (propriedades funcionais) são distintas de aspectos operacionais como protocolos de comunicação, requisitos de QoS (Quality of Service), ou propriedades não-funcionais, como a coordenação, comunicação, distribuição [Szt99], ou qualquer outro aspecto não coberto na descrição funcional. Arquiteturas de software permitem que projetistas concentrem-se nos aspectos funcionais da aplicação, podendo encapsulá-los nos componentes, e separadamente nos não-funcionais, podendo encapsulá-los nos conectores.
A definição e especificação dos elementos de uma arquitetura – componentes, conectores, portas – é feita através de uma ADL. Nela, tipos abstratos para componentes e conectores são especificados, bem como a instanciação e o estabelecimento da configuração dos mesmos.
Nas próximas seções procuramos tratar os elementos fundamentais de uma arquitetura de software (componentes, conectores e portas), as linguagens de descrição de arquiteturas (ADLs) e o processo de configuração.
2.1.1 Elementos de uma Arquitetura
2.1.1.1 Componentes
Um componente refere-se a um processo, objeto, procedure, ou qualquer pedaço de código ou dados identificável. Podem corresponder a unidades de
compilação de linguagens de programação convencionais e objetos tais como arquivos [SDZ96]. Componentes podem ainda, no nível de linguagens de programação, ser representados por módulos, classes, objetos ou um conjunto de funções [BMR96].
Em [DW92] há uma distinção entre três tipos de componentes, chamados elementos: (i) elementos de processamento; (ii) elementos de dados e (iii) elementos de conexão. Elementos de processamento fornecem serviços de transformação dos elementos de dados, os quais possuem informações. Elementos de conexão, que podem ser elementos de processamento, elementos de dados ou ambos, constituem a ligação (glue) que mantém diferentes partes conectadas. Neste caso, componentes do tipo elementos de conexão referem-se a conectores.
Componentes em geral possuem uma interface através da qual podem fornecer serviços e requerer serviços (definidos por cláusulas do tipo import/export ou
provides/requires). No nível da linguagem de programação, por exemplo, procedures
ou functions definidas podem representar serviços oferecidos e chamadas podem representar requerimentos de serviços.
Na descrição da arquitetura de um sistema de software, um componente deve funcionar como um tipo ou classe e possuir uma especificação que defina seus pontos lógicos de interação com outros componentes. Tais pontos de interação, chamados portas, representam a interface do componente, à qual pode incluir assinatura, funcionalidade e propriedades da interação.
As portas de um componente podem ser de dois tipos: de saída ou de entrada. Portas de saída representam serviços que podem ser requisitados pelo componente e portas de entrada associam-se a serviços fornecidos pelo mesmo
componente. A figura 2-1 mostra a representação de dois componentes de um possível sistema cliente-servidor simples. Cliente possui uma porta de saída e Servidor uma porta de entrada. porta de entrada porta de saída Cliente Servidor (b) (a)
Figura 2-1 Representação de componentes. (a) Cliente tem uma porta de saída. (b) Servidor tem uma porta de entrada.
2.1.1.2 Conectores
A interação entre os componentes em uma arquitetura de software é governada por um ou mais conectores. Em princípio, eles encapsulam a funcionalidade necessária à interação sem que os componentes envolvidos tenham suas interfaces modificadas ou adaptadas para esse fim.
Além de intermediar a interação entre os componentes, os conectores especificam quaisquer mecanismos auxiliares de implementação [SDZ96]. Dentre outros mecanismos, podem representar dados compartilhados, chamadas remotas (RPC – Remote Procedure Call), fluxos de dados, protocolos de comunicação, protocolos de interação, modelos de sincronização e concorrência.
Existem duas formas de se modelar conectores: implicitamente e explicitamente. Conectores implícitos não podem ser nomeados e tipados, e são
especificados apenas no contexto da configuração [MT97]. As ADLs Rapide [LKA95] e Darwin [MKS90, MK96] modelam conectores de forma implícita.
Os conectores modelados explicitamente são entidades de primeira classe, ou primeira ordem, e estão presentes nas ADLs Babel R-RIO [Mal96], UniCon [SDZ96] e Wright [AG97], dentre outras. Conectores de primeira ordem podem ser tipados e instâncias dos mesmos podem ser criadas para descrever um sistema particular, melhorando a reutilização.
Em [MT97] há um estudo comparativo entre ADLs e são discutidas características de ambas as formas de modelagem de conectores, quanto à definição de interface, tipos, semântica e evolução.
Para a proposta apresentada nesta dissertação, tratamos os conectores como elementos de primeira ordem, por terem propriedades similares aos componentes: (i) podem funcionar como tipos; (ii) possuem interface conforme a relação
requires/provides; (iii) podem especificar pontos de interação (portas). Durante o
processo de configuração, instâncias de um mesmo tipo de conector podem ser criadas para intermediar as interações entre instâncias de componentes. A configuração é realizada interligando-se as portas de um conector às portas dos componentes envolvidos. Portas de conectores são denominadas roles em determinadas ADLs, como a ACME [GMW97], Aesop [GAO94], Unicon [SDZ96] e Wright [AG97].
Conectores possuem características que os tornam especialmente atraentes para tratar os aspectos relacionados com a interação e a comunicação entre componentes [BF96]. Dentre estas características destacam-se:
- o conhecimento da interface e as referências dos componentes interligados por eles;
- a capacidade de examinar o conteúdo de requisições e respostas antes de encaminhá-las aos seus destinos finais;
- a possibilidade de controlar e manipular o repasse das requisições e respostas para os seus destinos;
- a sua similaridade com componentes quanto às propriedades de configuração, facilitando sua criação através da composição de outros conectores.
Como exemplo consideremos a arquitetura da figura 2-2 para um sistema cliente-servidor simples, no qual os componentes tem suas interações governadas por um conector. Cliente e Servidor têm suas portas interligadas às portas do conector C-S, tornando possível a interação entre os mesmos. As requisições de Cliente, executadas através da porta de saída, são interceptadas e encaminhadas ao componente Servidor.
provide request
Servidor C-S
Cliente
Figura 2-2 Arquitetura de um sistema cliente-servidor simples. Componentes Cliente e Servidor têm sua interação intermediada pelo conector C-S.
2.1.2 Descrição Arquitetural
A descrição de uma arquitetura é feita através de uma ADL –
Architecture Description Language. Diversas ADLs e ferramentas de suporte têm sido
desenvolvidas com o objetivo de modelar arquiteturas, tanto em relação a aplicações de domínios particulares quanto de propósito geral [MT97]. As ADLs fornecem um
framework conceitual e uma sintaxe concreta para a caracterização de arquiteturas de
software. Tipicamente oferecem ferramentas para compilar, analisar ou simular descrições arquiteturais escritas em suas linguagens associadas [GMW97].
Com o objetivo de ilustrar uma descrição arquitetural, apresentamos no código 2-1a descrição da arquitetura do sistema cliente-servidor simples da figura 2-2, utilizando a ADL Babel, empregada no ambiente de suporte R-RIO
(Reflective-Reconfigurable Interconnectable Objects) [LLS99, Lob99]. Tal arquitetura possui dois
componentes e apenas um conector. Entretanto, ADLs normalmente suportam arquiteturas cujos componentes interagem através de um ou mais conectores interligados.
A descrição começa com a definição das portas Request e Provide
seguida da definição dos tipos para os componentes Servidor e Cliente, denominados módulos (module) no R-RIO. Na linha 13 está a descrição do tipo para o conector C-S.
Importante observar que as portas de entrada e de saída do conector tem a mesma descrição das portas de Servidor e Cliente.
A linha 18 apresenta um conjunto de instâncias de Servidor, Cliente e C-S. Estas são as entidades reais que são configuradas na próxima linha. No exemplo
existe uma única instância de Servidor (servidor), uma única de Cliente (cliente) e uma única de C-S (cs). 1 module ClienteServidor { 2 port Request; 3 port Provide; 4 5 module Servidor { 6 inport Provide; 7 } servidor; 8 9 module Cliente { 10 outport Request; 11 } cliente; 12 13 connector C-S { 14 inport Request; 15 outport Provide; 16 } cs; 17
18 instantiate servidor, cliente, cs; 19 link cliente to servidor by cs; 20
21 } cliente_servidor; 22
23 start cliente_servidor;
Código 2-1 Descrição da arquitetura do sistema cliente-servidor simples em Babel.
Finalmente, as instâncias dos componentes e do conector são combinadas, associando-se as respectivas portas de cada uma delas. Babel permite que as portas sejam suprimidas na instrução link. Dessa forma, o conector adapta-se aos
módulos envolvidos. Conectores do R-RIO são adaptáveis ao contexto
(context-reflective) [LLS99] no sentido que, quando plugados entre módulos, podem ser
dinamicamente adaptados (em tempo de configuração) para intermediar as interações. A instrução, incluindo-se as portas, seria
ou ainda,
link cliente.Request to cs.Request; link servidor.Provide to cs.Provide;
As duas últimas instruções interligam módulo e conector tendo como referência as portas de ambos.
2.1.3 Implementação
Uma arquitetura é definida a partir de tipos para componentes e conectores. Durante o processo de configuração, as instâncias devem ser criadas. Componentes e conectores precisam ser implementados em alguma linguagem de programação para que torne a aplicação em desenvolvimento um sistema executável.
Algumas ADLs geram aplicações, como por exemplo Darwin [MKS90, MK96] e UniCon [SDZ96]. Estas, entretanto, exigem implementações preexistentes de componentes nas linguagens C++ e C respectivamente [MT97]. Rapide [LKA95] também pode construir sistemas em C e C++, enquanto C2 [Med99] gera sistemas em Java e C++.
R-RIO realiza o mapeamento de tipos de componentes e conectores para classes Java. O ambiente oferece uma estrutura na qual classes Java, representantes de componentes e conectores, são instanciadas em hosts no sistema distribuído. O sistema de software configurado pode ter seus componentes iniciados, parados, bloqueados e reiniciados.
Além de componentes e conectores, a implementação de uma arquitetura pressupõe o aspecto relacionado à forma com a qual as instâncias desses elementos
devem interagir para estabelecer a configuração. Por exemplo, em C2 o conector é associado a mecanismo de roteamento, responsável por encaminhar mensagens. No R-RIO, as portas – pontos lógicos de acesso – são representadas pelas operações invocadas ou definidas pelos componentes. Outro exemplo é o UniCon, onde propriedades são definidas para portas, conectores e componentes da arquitetura através de Listas de Propriedades. A presente dissertação enfoca esse aspecto, apresentando a proposta de um design pattern para auxiliar na organização de componentes e conectores, no nível da implementação, de acordo com a programação da configuração.
2.2 Patterns
Projetos arquiteturais, quando bem definidos, fornecem um nível de abstração no qual projetistas podem analisar o comportamento do sistema como um todo sem a necessidade de aterem-se aos detalhes de implementação. A abstração auxilia na evolução durante o ciclo de vida do sistema, uma vez que pode facilitar possíveis alterações sem o comprometimento da integridade do mesmo.
Uma forma de se conseguir bons projetos arquiteturais é o reaproveitamento da experiência adquirida de outras arquiteturas concebidas. A questão central é como reaproveitar esta experiência, sem utilizar-se de práticas relacionadas à transmissão informal de idiomas arquiteturais. Por exemplo, um sistema pode ser definido, no nível da arquitetura, como um “sistema cliente-servidor”, um “pipeline”, ou um “sistema em camadas”. Todos estes termos são bastante genéricos e não conseguem expressar com exatidão as propriedades que poderiam ser reaproveitadas em outras arquiteturas.
Alguns idiomas arquiteturais constituem o que pesquisadores denominam “estilos arquiteturais” [MKM97]. Um estilo arquitetural caracteriza-se por uma família de sistemas que estão relacionados por propriedades semânticas e estruturais compartilhadas [AAG93]. Estilos fornecem [SG96] (i) um vocabulário de tipos de componentes e conectores, tais como pipes, clientes, servidores, etc.; (ii) um conjunto de restrições e como elas podem ser combinadas, por exemplo a especificação de que uma arquitetura cliente-servidor deve suportar n clientes e um servidor; (iii) modelos semânticos que especifiquem como determinar as propriedades do sistema como um todo a partir das propriedades de seus elementos.
Estilos arquiteturais são portanto padrões de organização estrutural que podem ser reaproveitados em novos sistemas. Uma vez conhecido o estilo pipeline, por exemplo, é suficiente empregá-lo na arquitetura de um determinado sistema de software.
Os estilos arquiteturais, como descritos, podem ser visualizados como tipos de patterns [Sha95, Sha96]. Patterns descrevem a experiência de projetistas quando do desenvolvimento de sistemas de software, seja no nível da arquitetura, do
design, ou da implementação.
Determinado projetista ao utilizar de forma recorrente o estilo pipeline de comunicação entre processos, por exemplo, pode compartilhar sua experiência com outros projetistas, definindo um pattern. Este pattern deve descrever o contexto ou situação cujo pipeline pode ser empregado, o problema que ele deve resolver e a solução encontrada (formato Contexto-Problema-Solução). Outros projetistas, ao lerem a definição do pattern, poderão empregá-lo em seus projetos de software.
Uma descrição completa do pattern Pipes and Filters está disponível em [BMR96]. Os pipes usados em sistemas Unix [Bac86], para transportar um fluxo de dados de um processo para outro, são exemplos imediatos do emprego desse pattern. Outro pattern bastante conhecido é o Layers [BMR96] que auxilia na estruturação de aplicações que podem ser decompostas em camadas, onde cada uma delas está em um nível de abstração. Um exemplo de modelo arquitetural que emprega o Layers é o Modelo de Referência OSI (Open Systems Interconnection) baseado em uma proposta desenvolvida pela ISO (International Standardization Organization) [Tan97].
2.2.1 Definição e Fundamentos
Os princípios que envolvem os patterns têm relação direta com a reutilização de software, mais especificamente com a reutilização da experiência no desenvolvimento de software. A cada projeto criado, especialistas adquirem experiência e conhecimento que naturalmente são empregados em novos projetos. Para casos onde boas soluções são encontradas, testadas e aplicadas, existem grandes chances de serem novamente empregadas com sucesso em contextos semelhantes àqueles já experimentados.
Boas soluções podem ser compartilhadas, transformando-se em padrões utilizados de forma recorrente. Há livros sobre algoritmos, por exemplo, que destacam soluções eficientes para problemas relacionados à classificação de elementos (e.g., sort), ou ainda que mostram como realizar, da melhor maneira possível, a recuperação de determinado item de um conjunto de elementos. Tais algoritmos foram exaustivamente analisados, implementados e testados por vários programadores, e formaram padrões que podem ser reutilizados por outros profissionais.
A idéia acima pode ser aplicada em todos os níveis do desenvolvimento de um sistema de software, desde a concepção de sua arquitetura até a implementação. Entretanto, um padrão, ou pattern, deve ser descrito de forma padronizada, apresentando o problema e sua respectiva solução. Assim, o projetista ao descrever um
pattern, pode compartilhar sua expertise com outros tornando possível a reutilização de
sua experiência quanto ao problema-solução descrito.
A documentação de um pattern é feita a partir da observação de sua ocorrência em diversos projetos de software. Este tipo de documentação é interessante pois o conhecimento, restrito apenas a alguns especialistas, é capturado e moldado de uma forma que possa ser facilmente compartilhado.
Importante frisar que patterns não são construções apenas teóricas, mas sim artefatos que são descobertos em múltiplos sistemas [Ris99]. Existe uma regra quanto à documentação de determinado pattern: sua solução deve ter sido aplicada em pelos menos três sistemas – chamada “Regra de Três” [Bra00]. Tal regra garante que um pattern documenta uma solução já utilizada em situações reais, e não apenas uma boa idéia ainda sem aplicação.
Uma definição para software pattern, ou simplesmente pattern, está em [BMR96]: um pattern descreve de forma padronizada um problema recorrente dentro de contextos específicos e apresenta um esquema genérico para sua solução. A solução é especificada através da descrição de seus componentes, incluindo suas responsabilidades, relacionamentos e a forma com a qual colaboram uns com os outros.
(1) Um pattern trata um problema recorrente que aparece em situações específicas. Por exemplo, o problema de processar fluxos de dados (tratado pelo pattern Pipes and Filters) é recorrente e pode surgir quando do desenvolvimento de sistemas de software nos quais a interação entre processos é necessária. Processos podem interagir através de pipes que conduzem os dados de um processo para o outro.
(2) Patterns documentam a experiência empregada em projetos de
software, tornando-a disponível a outros projetistas. Eles portanto não são invenções, mas sim meios de reutilizar o conhecimento obtido por projetistas experientes quando do desenvolvimento de sistemas de software.
(3) Patterns identificam e especificam abstrações que estão acima do
nível de classes, instâncias ou componentes individuais. Normalmente, um pattern descreve vários componentes, classes ou objetos e detalha suas responsabilidades e relacionamentos, bem como suas cooperações. Componentes em conjunto, portanto, resolvem o problema tratado por um pattern.
(4) A descrição de um pattern deve seguir o formato Contexto-Problema-Solução, apresentando o contexto no qual o problema deve ser tratado, o problema a ser resolvido e a correspondente solução.
O livro Design Patterns: Elements of Reusable Object-Oriented Software [GHJ95] foi o primeiro a ter aceitação como uma forma padronizada de descrição de
patterns [Bra00]. Ele documenta vinte e três design patterns (um tipo de pattern) que
podem ser aplicados no desenvolvimento de sistemas orientados a objeto.
2.2.2 Tipos de Patterns
Patterns podem ser descritos nas diversas fases do desenvolvimento de
um sistema de software. Alguns deles podem auxiliar na estruturação de um sistema em subsistemas; outros suportam o refinamento de subsistemas, componentes ou conectores. Os patterns também auxiliam na fase de implementação, através da descrição de problemas e soluções relacionados a determinadas linguagens de programação.
Uma forma de categorizar patterns relaciona-se às diferentes fases de desenvolvimento de um sistema de software [RZ96, BMR96]. As categorias são:
architectural patterns, design patterns e idiomas.
2.2.2.1 Architectural Patterns
Architectural Patterns são patterns relacionados à estruturação do
sistema de software partindo-se da concepção de sua arquitetura. Eles são modelos (templates) utilizados para a definição de arquiteturas de software concretas. Expressam um fundamento estrutural para sistemas de software, fornecendo um conjunto de subsistemas predefinidos, especificando suas responsabilidades e incluindo regras para a organização dos relacionamentos entre eles [BMR96].
Quando da definição de uma arquitetura de software em particular, esse tipo de pattern pode ser empregado como um passo inicial. Ele pode ser analisado
quanto à sua solução e um mapeamento pode ser feito entre os subsistemas do pattern e os subsistemas necessários à arquitetura em definição. Subsistemas desta, que podem ser componentes e conectores, são associados com os descritos no pattern utilizado, fazendo com que a arquitetura tenha sua concepção fundamentada no mesmo.
Um exemplo de architectural pattern é o Pipes and Filters, já citado. Ele fornece uma estrutura para sistemas que processam um fluxo de dados. Cada passo do processamento é encapsulado em um componente chamado filtro, e os dados são passados através de pipes entre filtros adjacentes. Há no pattern uma estrutura definida com propriedades, variantes, conseqüências de utilização, etc. que são naturalmente reaproveitadas por novas arquiteturas. Layers também é um exemplo de architectural
pattern. O livro POSA2 [SSR00], um dos mais recentes catálogos de patterns, apresenta
várias descrições de architectural patterns, dentre eles o Interceptor, um dos patterns relacionados ao design pattern proposto nesta dissertação.
2.2.2.2 Design Patterns
Design Patterns caracterizam-se como os mais conhecidos e empregados
no desenvolvimento de sistemas de software. Tais patterns têm relação com a fase de projeto (design) do sistema, no qual subsistemas de arquiteturas de software e seus relacionamentos são refinados ou detalhados em unidades arquiteturais menores, representados por exemplo, por modelos de classes e objetos.
Um design pattern fornece um esquema para o refinamento de subsistemas (ou componentes) e seus relacionamentos (ou conectores). Ele descreve uma estrutura recorrente de comunicação entre componentes que resolva um problema geral dentro de um contexto particular [GHJ95].
Apesar de atuarem em um nível mais baixo que os architectural patterns,
design patterns são independentes de linguagens ou paradigmas de programação
[BMR96]. Eles podem, entretanto, ter a descrição de técnicas de implementação e características específicas relacionadas a alguma linguagem de programação, quando for o caso. Fragmentos de código podem também ser descritos, com o objetivo de ilustrar como poderia ser sua implementação em determinada linguagem de programação.
O design pattern Observer, descrito em [GHJ95] está resumido abaixo. Ele apresenta uma solução para o problema de implementar dependências entre objetos de um sistema.
Nome: Observer
Contexto: Um componente usa dados ou informações fornecidas por outro componente.
Problema: A mudança do estado interno de um componente pode introduzir inconsistências entre componentes que cooperam entre si. Para restaurar a consistência, precisamos de um mecanismo para trocar dados ou informações de estado entre tais componentes.
Duas restrições (forças) estão associadas com este problema:
- Os componentes devem ser fracamente acoplados; o fornecedor de informações não deve depender de detalhes de seus colaboradores. - Os componentes que dependem do fornecedor de informações não são
Solução: Implementar um mecanismo de propagação de modificações entre o fornecedor de informações (o observado ou subject) e os componentes dependentes dele (os observadores ou observers). Observadores podem dinamicamente registrarem-se nesse mecanismo. Sempre que o observado modifica seu estado, inicia o mecanismo de propagação de modificações para restaurar a consistência com todos os observadores registrados. Modificações são propagadas através da invocação de uma função especial de atualização, comum a todos os observadores.
2.2.2.3 Idiomas
Idiomas são patterns diretamente relacionados a aspectos de implementação específicos de uma linguagem de programação [BMR96]. Descrevem como implementar aspectos particulares de componentes ou conectores utilizando características de uma dada linguagem.
Idiomas capturam a experiência relacionada à programação e vinculada a linguagens específicas. Algumas vezes um idioma útil para uma linguagem de programação não faz sentido para outra [BMR96]. Programadores C++, por exemplo, usam idiomas para gerenciar dinamicamente a alocação de recursos, o que em Java não há emprego, uma vez que ela fornece um mecanismo automático para a coleta de lixo (garbage collection). Idiomas para a linguagem C++ podem ser encontrados em [Cop92].
2.2.3 Descrição de Patterns
Para que um pattern possa ser utilizado no processo de desenvolvimento de sistemas de software é necessário que seja compreendido quanto à sua solução para determinado problema. Patterns portanto devem ser apresentados de uma forma apropriada no sentido de fornecer uma descrição clara do problema tratado e da solução proposta. Sua descrição também deve contemplar os detalhes necessários à sua implementação e considerar as conseqüências de sua aplicação. Descrições de patterns são informais, mas ao mesmo tempo estruturadas e precisas. Devem descrever por que e como usá-los, em vez de apenas documentar o que são.
A estrutura Contexto-Problema-Solução, utilizada na descrição do
pattern Observer na seção anterior, é um bom ponto de partida para a descrição de um pattern, uma vez que oferece características essenciais e idéias centrais do mesmo.
Entretanto tal estrutura não é suficiente, devendo ser complementada com exemplos, questões relacionadas à implementação, conseqüências da aplicação do pattern, detalhamentos quanto à colaboração entre os componentes, classes ou objetos envolvidos. Estas são algumas informações que enriquecem a descrição do pattern, tornando-a mais clara e facilitando sua utilização no desenvolvimento de sistemas.
O formato de descrição de patterns mais conhecido é chamado de “formato GoF”, utilizado em [GHJ95]. Independentemente do formato utilizado na sua descrição, um pattern deve conter certos elementos essenciais. Dois patterns (Mandatory Elements Present e Optional Elements When Helpful) publicados em [MRB97] apresentam, respectivamente, os elementos considerados obrigatórios e os considerados opcionais na descrição de um pattern. O primeiro mostra ainda a relação entre os elementos obrigatórios, apresentada na figura 2-3.
Os elementos obrigatórios são: Nome (Name), Contexto (Context), Problema (Problem), Forças (Forces) e Solução (Solution). Os elementos opcionais são, dentre outros: Conseqüências (Consequences), Patterns Relacionados (Related
Patterns), Usos Conhecidos (Know Uses) e Código-Exemplo (Sample Code). Os nomes
exatos desses elementos podem variar de uma descrição para outra, bem como a ordem de apresentação dos mesmos.
tem resolve prioriza opera em Usuário do Pattern Contexto Força Solução Problema resolve
Figura 2-3 Relacionamento entre elementos da descrição de um pattern.
Nome (Name)
Nome pelo qual o par problema/solução pode ser referenciado. Um nome significativo permite a formação de um vocabulário que auxilie nas discussões conceituais do pattern em questão. Em casos onde o pattern possui outro(s) nome(s) pelo(s) qual(is) é conhecido, o elemento Também Conhecido Como (Also Known As) é utilizado para referenciá-los.
Contexto (Context)
Circunstâncias nas quais o pattern pode ser aplicado. Este elemento mostra a aplicabilidade do pattern e pode ser visto como a configuração inicial do sistema antes da aplicação do mesmo. Contexto é substituído por Aplicabilidade (Applicability) no formato GoF.
Problema (Problem)
O problema específico que precisa ser tratado e resolvido. Um exemplo concreto do problema pode ser descrito através da ilustração de modelos de classes, objetos, etc. Tal exemplo pode aparecer como uma seção distinta chamada Exemplo (Example), auxiliando no entendimento dos demais elementos da descrição do pattern. No formato GoF, Problema é substituído por Intento (Intent) acrescido da seção Motivação (Motivation) que enfoca um exemplo ilustrativo.
Forças (Forces)
Descrevem forças (restrições) que devem ser levadas em conta quando da escolha de uma solução para o problema, e como interagem ou entram em conflito com os objetivos almejados pelo pattern. Alguns patterns têm as suas forças descritas junto com Problema.
Solução (Solution)
Solução proposta para o problema. A descrição da solução pode incluir figuras, diagramas, textos que identifiquem a estrutura do pattern, seus participantes e
colaborações entre eles. A solução deve ser descrita levando-se em conta as forças definidas para o pattern.
Na descrição de design patterns utilizando o formato GoF, as seções Estrutura (Structure), Participantes (Participants), Colaborações (Collaborations) e Implementação (Implementation) substituem o elemento Solução.
A seção Estrutura mostra uma representação gráfica de classes baseada em UML [BRJ99]. Participantes, por sua vez, é uma seção que mostra as classes e/ou objetos do pattern e suas responsabilidades. Em alguns patterns, esta seção traz cartões CRC (Class-Responsibility-Collaborator) ou CRC cards [BC89, Amb98], descrevendo as classes com suas responsabilidades e colaboradores. A seção Colaborações apresenta como os participantes colaboram entre si de acordo com suas responsabilidades, e finalmente, Implementação mostra as técnicas envolvidas na implementação do pattern.
Conseqüências (Consequences)
Esse elemento descreve as conseqüências geradas e os benefícios obtidos a partir do emprego do pattern que está sendo definido.Essa descrição é feita a partir da análise da composição do sistema resultante após a aplicação do respectivo pattern. A comparação entre os elementos Conseqüências e Forças permite verificar se os objetivos do pattern foram atendidos. O nome Contexto Resultante (Resultant Context) substitui Conseqüências em muitas descrições de patterns.
Patterns Relacionados (Related Patterns)
Outros patterns de interesse que tratem problemas similares ou ainda que auxiliem na composição do pattern que está sendo descrito. O elemento Patterns Relacionados pode ainda apresentar patterns que resolvam problemas mostrados em Conseqüências, quando for o caso.
Usos Conhecidos (Known Uses)
Descreve ocorrências conhecidas do pattern e suas aplicações em sistemas existentes. Auxilia na validação do pattern no sentido de apresentar uma solução para um problema recorrente.
Código-Exemplo (Sample Code)
Descreve um exemplo de código que apresente como implementar o
pattern. Fragmentos de código são mostrados em uma determinada linguagem de
programação. Os design patterns apresentados em [GHJ95] têm os códigos escritos na linguagem C++ ou Smalltalk.
2.3 Patterns em Arquiteturas de Software
O desenvolvimento de software a partir da concepção de sua arquitetura pode envolver o emprego de patterns.
Patterns, como visto, foram construídos a partir das experiências de
desenvolvimento de software. Existem patterns baseados em princípios básicos de desenvolvimento de programas estruturados, uma vez que patterns não se dedicam apenas à orientação a objetos. Há outros cujos objetivos são voltados à construção de sistemas de software reutilizáveis e preparados para o tratamento de propriedades não-funcionais [BMR96].
A experiência adquirida tem sido organizada e pode auxiliar em novas arquiteturas, projetos e programas de software. Os estilos arquiteturais que permitem a definição de componentes e conectores, restrições e regras de construção, podem ser descritos por patterns, no caso architectural patterns. A construção de sistemas de software utilizando frameworks - sistemas parcialmente completos – pode ser realizada através de patterns.
Quando da construção de sistemas de software, princípios fundamentais como abstração, encapsulamento, ocultamento de informações, modularização, separação de interesses, acoplamento e coesão podem ser aplicados através dos
patterns, inteiramente baseados nesses princípios.
Os patterns fundamentam-se em princípios observados durante toda a evolução das áreas relacionadas à construção de software. Oferecem importantes contribuições à área de arquitetura através do suporte à construção e à evolução de sistemas de software de qualidade com propriedades funcionais e não-funcionais bem definidas.
As próximas subseções associam patterns com estilos arquiteturais e
frameworks, além de apresentar como podem ser organizados em catálogos, sistemas e
2.3.1 Estilos Arquiteturais
Um estilo arquitetural define uma família de sistemas de software em termos de sua organização estrutural. Expressa componentes e conectores, restrições e regras para sua construção. A noção de estilo arquitetural foi introduzida por Dwayne E. Perry e Alexander L. Wolf em 1992, como mencionado em [BMR96]. Estilos arquiteturais também são abordados em [SG96].
Architectural patterns podem ser empregados para descrever estilos
arquiteturais e, portanto, auxiliar na construção de arquiteturas. Na prática, o arquiteto de software adota um ou mais desses patterns para moldar sua arquitetura.
2.3.2 Frameworks
Patterns têm sido utilizados na construção de sistemas baseados em frameworks. Frameworks são sistemas de software parcialmente completos e que
definem pontos onde as adaptações para funcionalidades específicas podem ser feitas. Um framework portanto, é uma estrutura ainda “não terminada”, com o objetivo de auxiliar na implementação de um sistema com mesmo “domínio de problema” do framework. O desenvolvedor deste sistema pode utilizá-lo, diminuindo assim o tempo despendido e incrementando a reutilização.
Em um ambiente orientado a objeto, um framework consiste de um conjunto de classes abstratas e concretas que cooperam mutuamente umas com as outras [Bud96], compondo a infra-estrutura necessária ao desenvolvimento de um sistema específico. Quando da criação do sistema de software concreto, pontos de
especialização do framework, chamados hot spots, devem ser adaptados para fornecer as funcionalidades exigidas pelo sistema em construção. Além de técnicas como herança ou polimorfismo, design patterns podem ser empregados nestes pontos [BMR96].
Design patterns têm emprego na documentação de um framework,
auxiliando na descrição de seus propósitos, de como utilizá-lo e dos detalhes de seu projeto [Joh92]. Com isso, um alto nível de comunicação pode ser alcançado entre o projetista e o usuário do framework [Sri99].
2.3.3 Linguagem de Pattern (Pattern Language)
Patterns não existem de forma isolada, uma vez que há muitas
interdependências entre eles. Uma linguagem de pattern, ou pattern language, é uma coleção de patterns que atuam em conjunto para resolver problemas em um dado domínio [Ris99]. Além da descrição dos patterns, há a descrição de regras para combiná-los dentro de um estilo arquitetural. As linguagens de pattern descrevem famílias de sistemas relacionados [Bra00].
Regras e roteiros que explicam como e quando aplicar os patterns compõem uma linguagem de pattern. Dentro de um domínio de problemas, uma linguagem enumera os patterns com suas descrições e formas, e quando, onde, e em qual ordem aplicá-los. Em uma mesma linguagem de pattern podem haver architectural
patterns, design patterns e idiomas abrangendo as diversas fases de desenvolvimento de
um sistema de software.
Assim, as linguagens de pattern guiam analistas de sistemas, arquitetos, projetistas e implementadores a produzir sistemas apropriados a resolver problemas
organizacionais e de desenvolvimento em níveis de escala e diversidade variados [Bra00].
Além das linguagens, existem duas outras formas de se agrupar patterns de acordo com os graus de estrutura e interação [Bra00]: catálogos (pattern catalog) e sistemas (pattern system).
Catálogo de Patterns (Pattern Catalog)
Um catálogo é uma coleção de patterns relacionados e que tipicamente subdivide os mesmos em um pequeno número de categorias. O inter-relacionamento de patterns quando tratado na forma de catálogos não é tão coeso e formal quanto na forma de linguagem [Bra00]. Os patterns são descritos, mas não a forma de inter-relacioná-los. Um dos mais conhecidos catálogos está descrito em [GHJ95]. É organizado distinguindo-se seus design patterns em três categorias: de criação, de estrutura e de comportamento.
Sistema de Pattern (Pattern Systems)
Um sistema de pattern é um conjunto coeso de patterns relacionados que atuam no suporte à construção e evolução de arquiteturas. Um sistema é organizado em grupos e subgrupos relacionados e descreve como os patterns podem ser combinados e compostos para a solução de problemas mais complexos.
Como definido em [BMR96], um sistema de pattern para arquitetura de software é uma coleção de patterns com receitas para sua implementação, combinação e utilização prática no desenvolvimento de software.
A partir da definição percebe-se que um sistema de pattern descreve os
patterns em si, e também como conectá-los ou integrá-los com outros do sistema.
Tanto sistemas quanto linguagens de pattern formam conjuntos coerentes de patterns fortemente entrelaçados com o objetivo de descrever a solução de problemas em um domínio particular. Entretanto uma linguagem de pattern para arquitetura de software deve cobrir todos os aspectos do domínio de problemas tratado, necessitando ser computacionalmente completa: pelo menos um pattern deve estar disponível para cada aspecto da construção e implementação do sistema. Linguagens auxiliam no mapeamento da arquitetura para o projeto na construção de um sistema de software [KC97].
Sistemas de pattern, por sua vez, descrevem apenas certos aspectos da construção de arquiteturas de software. Sua totalidade não é computacionalmente completa, entretanto representam mais que somente um catálogo de patterns, pois descrevem como os patterns são inter-relacionados.
Segundo [Bra00], na prática as diferenças entre linguagem e sistema de
pattern podem ser difíceis de verificar. Entretanto a diferença fundamental talvez esteja
na forma de utilização dos tipos de coleção de patterns: determinado catálogo, uma vez construído, pode evoluir para um sistema, o qual pode ainda evoluir para uma linguagem de pattern, apropriada ao domínio de problema enfocado pela arquitetura de software [Bra00].
2.4 Conclusão
Fundamentos e conceitos de arquiteturas de software e patterns foram apresentados neste capítulo. Tanto arquiteturas quanto patterns têm como objetivo principal maximizar a reutilização de sistemas de software.
Arquiteturas de software possibilitam, dentre outros benefícios, a separação de interesses, essencial na busca da reutilização. Requisitos funcionais e não-funcionais podem ser separados através do processo de configuração, no qual componentes podem implementar requisitos funcionais e conectores podem implementar requisitos não-funcionais.
A evolução de sistemas é facilitada, fazendo com que componentes e conectores possam ser adicionados, removidos ou mesmo modificados sem que os demais elementos precisem, necessariamente, ser alterados.
Esses benefícios entretanto podem ser prejudicados se a implementação da configuração arquitetural for complexa ou custosa. A minimização deste problema pode dar-se através da aplicação do conceito de patterns. Com patterns é possível disseminar a experiência adquirida quando do desenvolvimento de um sistema de software, elevando assim sua reutilização.
O processo de configuração, quando implementado, apresenta uma certa recorrência. O fato de instâncias de componentes serem interligadas com instâncias de conectores, com o objetivo de se comunicarem, podem formar uma espécie de “rede” de conectores intermediando esta interação. Componentes podem ser módulos ou pedaços de código com chamadas a outros módulos e com procedimentos ou funções declarados,
caracterizando alguma oferta de serviços. Tanto componentes quanto conectores possuem portas, representando suas interfaces.
Nos próximos capítulos desta dissertação apresentamos uma proposta, descrição e exemplos, de um design pattern destinado a auxiliar na implementação da configuração de arquiteturas de software.
38
%
!
A arquitetura de software de uma aplicação é criada partindo-se de três elementos fundamentais, como foi visto: componentes, conectores e portas. Através de uma ADL estes elementos são definidos na forma de tipos e, no caso de componentes e conectores, instanciados uma ou mais vezes na especificação arquitetural. Além de definir e instanciar componentes e conectores, é necessário realizar a configuração da arquitetura, criando assim a estrutura adequada à execução da mesma.
Este capítulo apresenta nossa proposta de um design pattern dedicado à implementação da configuração arquitetural. O design pattern, denominado
Architecture Configurator, parte da necessidade de componentes interagirem, uns
requerendo serviços e outros fornecendo serviços, privilegiando a reutilização de software através da separação de requisitos. A solução que permite a separação de