Estratégias de Persistência em Software Orientado a Objetos: Definição e Implementação de um Framework para Mapeamento Objeto-Relacional João Carlos da Silva, Marcelo Santos Daibert, Valdete Maria Gonçalves de Almeida, Alessandreia Marta de Oliveira Julio, Marco Antônio Pereira Araújo [email protected], [email protected], [email protected], {amojulio, maraujo}@granbery.edu.br Faculdade Metodista Granbery Bacharelado em Sistemas de Informação Rua Batista de Oliveira, 1145 – CEP 36010-530 – Juiz de Fora/MG Resumo A orientação a objetos, com o passar dos anos, vem se tornando o padrão de desenvolvimento de sistemas de software. Entretanto, os desenvolvedores enfrentam problemas quando necessitam persistir objetos, uma vez que os bancos de dados puramente orientados a objetos não são maduros o suficiente e normalmente com desempenho inferior aos bancos de dados relacionais. Vislumbrando este problema, este trabalho se propõe a realizar um estudo exploratório e comparativo das tecnologias de persistência de objetos disponíveis no mercado, incluindo as de banco de dados orientado a objetos e objeto-relacionais, além de técnicas de mapeamento objeto-relacional em banco de dados relacionais. Através destes estudos, foi definida e implementada uma ferramenta de persistência de objetos compatível com o Borland Delphi: o framework de persistência objeto-relacional DPL (Delphi Persistent Layer). Palavras Chave: Camada de Persistência, Orientação a Objetos, Mapeamento ObjetoRelacional, Delphi, DPL Abstract Object-Oriented has become, throughout the years, the development pattern for software systems. However, developers have faced problems whenever they need to persist objects, once purely OO database are not mature enough and normally with inferior yielding to relational database. In respect to this problem, it has been proposed to carry out an explanatory and comparative study of technologies of persistence of objects available in the market, including the OO database and the relational-object ones, besides the object-relational mapping techniques in relational database. By means of these studies, it has been defined and implemented a tool of persistence of objects compatible with Borland Delphi: the object-relational persistence framework DPL (Delphi Persistence Layer). Keywords: Persistence Layer, Object-oriented, Object-relational mapping , Delphi, DPL. 1. Introdução O paradigma de desenvolvimento orientado a objetos tem sido extensivamente utilizado na construção de sistemas de software e se tornando, de fato, o padrão para o desenvolvimento de aplicações. Os bancos de dados orientados a objeto são mais adequados para a persistência de objetos manipulados por aplicações orientadas a objeto, devido à utilização do mesmo paradigma. Porém a indisponibilidade atual destes bancos de dados, seja devido ao custo, diversidade ou amadurecimento do mercado, faz com que seja necessária a busca de alternativas para a realização da persistência. O modelo de armazenamento relacional é largamente utilizado e difundido no mercado, contando com diversos produtos e tecnologias maduras e eficientes. Sendo assim, existe uma forte tendência de utilização de bancos de dados relacionais para armazenamento dos objetos de aplicações orientadas a objetos. Porém, existem diferenças significativas do paradigma orientado a objetos para o modelo relacional. Para que seja possível obter os benefícios de ambos, é necessária uma compatibilidade entre esses modelos. Vislumbrando este problema, este trabalho se propôs a realizar um estudo exploratório e comparativo das tecnologias de persistência de objetos disponíveis no mercado, incluindo tecnologias de bancos de dados orientado a objetos, objetorelacionais, além de técnicas de mapeamento objeto-relacional em banco de dados relacionais. Foi realizado um levantamento de soluções de gerenciamento de banco de dados e de frameworks de mapeamento objeto-relacional para avaliação de viabilidade na utilização prática no desenvolvimento de projetos. Visando testar as soluções encontradas, foi implementado um sistema para estudo de caso compatível com as várias estratégias de persistência. Este estudo de caso surgiu de um processo de reengenharia de um sistema existente de controle acadêmico construído através de uma abordagem estruturada. Inicialmente foram analisados o banco de dados pós-relacional Caché [1, 2, 3, 4, 5], desenvolvido pela Intersystems e alguns frameworks de mapeamento objetorelacional do mercado [6, 7, 8]. Os estudados foram o DePO (Delphi Persistent Object) [9], o IO (Instant Objects) [10] e o TiOPF (TechInside Object Persistence Framework). Todos compatíveis com o ambiente Borland Delphi de Desenvolvimento. Com os dados obtidos a partir deste estudo realizado foi definida e implementada uma nova ferramenta de persistência de objetos compatível com o Borland Delphi: o framework de persistência objeto-relacional DPL (Delphi Persistent Layer) [11]. Diante disso, este trabalho está dividido em cinco seções além desta introdução. A seção 2 define mapeamento objeto-relacional. Na seção 3 são apresentados os conceitos de camadas de persistência. A seção 4 apresenta a camada desenvolvida no escopo deste trabalho, a DPL. Já na seção 5 é apresentado um estudo de caso e, na seção 6, as considerações finais. 2. Mapeamento Objeto-Relacional O mapeamento objeto-relacional é uma abordagem que permite a construção de sistemas utilizando o paradigma orientado a objetos com a persistência destes objetos em bancos de dados relacionais. Utilizando-se de técnicas e estratégias específicas, é possível mapear classes com seus atributos e associações para o modelo relacional (Figura 1). Tabelas Classes Figura 1. Mapeamento Objeto-Relacional Utiliza-se uma abstração bastante intuitiva no sentido de que uma classe pode ser mapeada para uma tabela no banco de dados relacional e atributos da classe para campos da tabela. Porém, algumas diferenças entre os dois modelos, como OID (Object Identifiers - identificador de objetos), tipos de dados, herança e associações, demandam um estudo mais detalhado das estratégias de mapeamento. 2.1 OIDs (Object Identifiers) Existem várias estratégias para atribuir OIDs para objetos, inclusive pode-se criar uma ou mais classes cuja responsabilidade específica é a de atribuir OIDs para objetos, sendo estas estratégias separadas das classes que implementam as regras de negócio. É considerada uma boa prática de desenvolvimento separar a estratégia de atribuição de OIDs das classes de negócio, evitando utilizar um atributo qualquer da classe para ser o identificador. Identificadores que possuem um significado de negócio, certamente mudam em algum momento [12], pois as regras de negócio mudam freqüentemente, e o esforço necessário para realizar esta modificação pode ser imensurável. Dentre as várias formas de gerar OIDs, pode-se por exemplo utilizar recursos do próprio sistema gerenciador de bancos de dados para gerar números seqüenciais automaticamente e utilizá-los como OIDs. Pode-se utilizar conjuntos de números inteiros, criar algoritmos sofisticados ou utilizar algoritmos já existentes como GUID’s (Globally Unique IDentifier) ou UUID’s (Universal Unique Identifier) disponibilizados pela Microsoft e Digital Equipment respectivamente [12]. Uma importante questão em relação aos OIDs está na escolha da estratégia utilizada para gerá-los. A utilização de estratégias proprietárias pode limitar a utilização e portabilidade da aplicação, pois nem todas as estratégias são possíveis em todas as plataformas de hardware e software. Porém, implementar uma estratégia própria que garanta a unicidade dos OIDs em diversos ambientes, principalmente em ambientes cliente-servidor e de objetos distribuídos não é uma tarefa trivial, devido ao volume de transações concorrentes ou distribuição do gerador de OIDs. 2.2 Mapeamento de Classes em Tabelas O mapeamento de classes pode ser feito mediante a paridade entre classe e tabela, ou seja, uma classe é mapeada para uma tabela [12]. Este mapeamento direto de classes para tabelas representa a forma mais simples de mapeamento, tornando mais fácil o entendimento e a manutenção de uma aplicação. Com um modelo de classes bastante simples isto poderia ser feito. Porém, nem sempre é simples assim. No caso de uma estrutura hierárquica, várias classes podem ser mapeadas para uma tabela, como também uma classe pode ser mapeada para várias tabelas. Ainda, classes com atributos multivalorados ou compostos podem ser mapeadas para mais de uma tabela. 2.3 Mapeamento de Atributos em Colunas Ao tratar do mapeamento de atributos de uma classe para colunas em tabelas de um banco de dados relacional, deve-se levar em conta que os atributos podem ser de tipos de dados simples como inteiros, ponto flutuante, caracteres, boleanos e binários, mas também podem ser de tipos de dados complexos como tipos baseados em outras classes. Os atributos podem ser ainda multivalorados, o que viola as regras de normalização do modelo relacional. Além disso, podem existir atributos de controle ou utilizados em cálculos, que geralmente não necessitam ser mapeados [12]. Desta forma, os atributos simples podem ser mapeados diretamente para colunas em uma tabela, já os atributos complexos e multivalorados podem necessitar de tabelas adicionais para seu armazenamento. Estes atributos complexos geralmente possuem características recursivas, ou seja, são classes que possuem outros atributos e assim sucessivamente. 2.4 Mapeamento de Herança Existem fundamentalmente três estratégias para mapear herança em um banco de dados relacional [13], exemplificado pela Figura 2: uma tabela por hierarquia: mapear toda a hierarquia de classes para uma tabela, onde todos os atributos das classes da hierarquia são armazenados nesta única tabela. A desvantagem desta estratégia é que toda vez que um objeto da hierarquia for persistido no banco, é necessário persistir também os valores das demais classes vazios, causando uma grande quantidade de campos inutilizados. Entretanto o acesso ao banco para a manipulação dos dados é mais rápido, uma vez que todos os dados estão em somente uma tabela. É adicionada uma coluna na tabela que referencia qual o tipo do objeto, ou seja, de qual classe aqueles dados pertencem; uma tabela por classe concreta: cada classe concreta mapeada vira uma tabela com todos os atributos herdados das super classes abstratas. A vantagem desta estratégia é a facilidade de manipulação de dados, uma vez que todos os dados de cada classe estão em apenas uma única tabela. Como desvantagem, destaca-se que quando se modifica uma classe abstrata, é necessário modificar todas as tabelas geradas pelas classes filhas no modelo relacional; uma tabela por classe: cada classe é mapeada para uma tabela. Uma tabela por Uma Tabela por Classe Concreta hierarquia Uma Tabela por Classe Figura 2. Estratégias de Mapeamento para Herança 2.5 Mapeamento de Associações As associações entre classes no modelo orientado a objetos é conceitualmente bastante similar ao relacionamento entre tabelas no modelo relacional. Este fato permite que tais associações sejam mapeadas para relacionamentos, podendo utilizar chaves estrangeiras ou tabelas auxiliares [13]. 2.5.1. Associações do Tipo 1:1 A associação do tipo 1:1 entre classes é mapeada colocando o atributo identificador da classe referenciada na classe que o referencia, criando então o conceito de uma chave estrangeira no modelo relacional [13], como demonstrado na Figura 3. Figura 3. Mapeamento de Associação do Tipo 1:1 2.5.2. Associações do Tipo 1:n Da mesma forma que a associação do tipo 1:1, o relacionamento 1:n também é mapeado colocando o atributo identificador da classe referenciada na classe que o referencia, criando então o conceito de uma chave estrangeira no modelo relacional [14], como demonstrado na Figura 4. Figura 4. Mapeamento de Associação do Tipo 1:n 2.5.3. Associações do Tipo n:n Para mapear uma associação do tipo n:n, é necessário utilizar o conceito de tabela associativa, cujo propósito é manter o relacionamento entre duas ou mais tabelas do modelo relacional [13]. Cria-se então uma tabela associativa com os OID’s das classes que se referenciam, garantindo a navegabilidade do relacionamento, como exemplificado na Figura 5. Figura 5. Mapeamento de Associação do Tipo n:n 2.6 Mapeamento de Associações Todo/Parte As associações todo/parte geralmente são representadas como agregações ou composições. As agregações são associações que representam uma relação grupo/membro, ou seja, onde o objeto agregado pode existir independente dos objetos que o constituem. Já a composição representa um tipo de associação onde o todo não existe sem sua(s) parte(s). Neste caso, tanto agregações quanto composições são mapeadas para tabelas de duas formas: utilizando uma única tabela com todos os atributos da classe que representa o todo e das classes que representam suas partes, ou mais de uma tabela, uma para cada classe envolvida no modelo [14]. 3. Camadas de Persistência O termo camada é muito é utilizado por desenvolvedores de sistemas que desejam construir aplicações com maior grau de manutenibilidade, encapsulando funcionalidades e diminuindo assim o acoplamento dentro da aplicação. Na utilização do paradigma orientado a objetos, alguns padrões de projeto auxiliam na construção de aplicações em camadas, como por exemplo, o padrão Layer e o MVC (Model-ViewController), dentre outros [13]. Existem vários arranjos possíveis destas camadas, e a quantidade delas não é pré-determinada, dependendo do tamanho da aplicação e da arquitetura desejada. Porém, certamente, quanto menor o número de camadas, melhor é o desempenho da aplicação [13]. Desta forma o equilíbrio entre o número de camadas e sua arquitetura traz benefícios à manutenibilidade do sistema com um mínimo de impacto no seu desempenho. Já persistência é a habilidade de um objeto sobreviver ao ciclo de vida do processo no qual ele reside [15]. Objetos que morrem com o fim de um processo são chamados transientes. A Figura 6 apresenta uma arquitetura em camadas de uma aplicação utilizando o modelo MVC, que separa em camadas a aplicação, para diminuir a dependência entre as classes de interface e de negócio. Classes de Interface com usuário (View) Modelo MVC Classes de Controle (Controller) Classes de Negócio (Model) Classes de sistema Camada de Persistência Banco de Dados Figura 6. Arquitetura de um Aplicativo em Camadas A camada de persistência encontra-se entre a camada de negócio (onde estão as classes de domínio da aplicação, ou seja, classes que definem as regras de negócio) e o banco de dados, permitindo que o impacto das modificações em uma delas seja atenuado em relação à outra. Isto diminui o grau de dependência do banco de dados, aumentando o grau de manutenibilidade. Toda responsabilidade por persistir objetos fica a cargo da camada de persistência, liberando a aplicação destas tarefas, aumentando a produtividade no desenvolvimento [16]. Na camada de persistência está a definição das estratégias de mapeamento do modelo orientado a objetos para o modelo relacional, discutidas anteriormente. As estratégias de mapeamento podem estar implícitas nas classes que compõem a camada, porém neste caso, a escolha de determinada estratégia em detrimento de outra, traz uma inflexibilidade na própria camada, pois para mudar de estratégia é necessário modificar a camada. Porém, é possível isolar da camada de persistência as definições das estratégias de mapeamento, de forma que as estratégias possam ser modificadas ao longo do tempo, de acordo com as necessidades. A utilização de documentos XML (Extensible Markup Language) é uma das possíveis formas de armazenamento das definições das estratégias de mapeamento. Pode-se também utilizar arquivos texto ou até mesmo tabelas do próprio banco de dados como estratégia de mapeamento. 4. A Ferramenta DPL (Delphi Persistent Layer) Para este trabalho, o Delphi foi adotado como linguagem de desenvolvimento da DPL devido a sua adequação ao paradigma da orientação a objetos. Além disto, para este ambiente não existem tantas opções de persistência como para outros ambientes como Java. Isto dificulta a adoção efetiva do paradigma orientado a objetos no desenvolvimento de aplicações de Delphi. A construção da DPL seguiu uma abordagem de desenvolvimento através do ciclo de vida espiral, permitindo separar o processo de construção em etapas e de maneira evolutiva. Desta forma, funcionalidades são implementadas a cada iteração. Para divisão das fases foi considerado o modelo proposto por Scott Ambler para construção de camadas de persistência [13]. Baseado neste modelo, o desenvolvimento da DPL encontra-se concluído até a segunda etapa (milestone), o que permite a realização das operações básicas de persistência como: criar, recuperar, atualizar e excluir objetos (CRUD), além de tratar as associações de classes, como demonstrado na Figura 7. Fases de Evolução da Camada 6 DPL Final 5 4 3 2 1 Legenda: 1. Operações CRUD 2. Suporte para associações 3. Busca por critérios 4. Suporte para cursores, proxies, registros e cache 5. Aplicação de administração 6. Suporte a transações Figura 7. Fases de evolução da DPL A Figura 7 ilustra a evolução da DPL segundo um ciclo de vida em espiral, dividido em seis fases. Na primeira fase a DPL oferece as operações básicas de persistência (Create, Read, Update e Delete) que permite persistir e recuperar objetos individualmente. Na segunda fase é possível tratar associações dos objetos, podendo então persistir e recuperar de maneira simples objetos associados. Na terceira fase é possível determinar critérios para recuperação e processamento de conjuntos de objetos. Na quarta fase pode-se utilizar cursores, proxies, cache e registros para otimizar o desempenho da camada. Na quinta fase é implementada uma aplicação de administração da DPL que permita uma interação amigável ao desenvolvedor na configuração da DPL, definição das estratégias de mapeamento, construção automática de classes de domínio e tabelas do banco de dados relacional, entre outras. A sexta e última fase trata de implementação do controle de transações bem como concorrência. A lista de requisitos a seguir encontra-se implementada, onde alguns destes itens foram implementados parcialmente, porém de maneira completamente funcional, de forma que a DPL pode ser utilizada para a construção de aplicações simples orientadas a objetos [13]: suportar a arquitetura de aplicação em n-camadas; implementar o uso da tecnologia DBExpress para acesso aos bancos de dados relacionais; gerenciar conexões com o banco de dados relacional; realizar automaticamente o mapeamento de classes e atributos para tabelas e campos respectivamente no banco de dados relacional, sendo gerada uma tabela por classe existente na aplicação; mapear agregações, associações (um-para-um, um-para-n e n-para-n) e composições de classes para relacionamento entre tabelas no banco de dados relacional; mapear herança de classes para tabelas no banco de dados relacional; gerar automaticamente identificadores de objetos; implementar as operações básicas para os objetos persistentes, seguindo o padrão CRUD; recuperar todos os objetos persistentes automaticamente de uma classe; retornar coleções de registros como resultado de requisições de recuperações visando evitar overhead de conversão de registros de bancos de dados em objetos e depois em registros novamente. Para aplicações mais complexas é necessária a evolução da DPL, podendo seguir as demais fases propostas por Scott Ambler, com incorporação de outros requisitos descritos no projeto. Atualmente, aplicações que utilizam classes com ou sem herança, associações um-para-um e um-para-n são ideais para avaliação da DPL. A DPL foi projetada para atender à necessidade de troca do banco de dados relacional utilizado para armazenamento dos objetos, desde que este atenda ao padrão SQL ANSI. Desta forma, foi utilizado o componente DBExpress que permite, de maneira simples, a troca do driver utilizado na comunicação com o banco de dados. Embora o DBExpress dê uma certa flexibilidade à DPL, este exige por sua vez a utilização de drivers compatíveis com a sua tecnologia, o que é uma certa limitação. Devido ainda, à implementação de alguns requisitos estarem definidos em fases de desenvolvimento mais adiantadas, é necessária a criação e manutenção manual tanto do banco de dados e suas tabelas, bem como do arquivo XML que guarda informações do mapeamento objeto-relacional. O tratamento de tipos de dados não primitivos, assim como requisitos relacionados a desempenho, concorrência e transações, estão alocados para implementação em fases mais adiantadas na construção da DPL. 4.1 Armazenamento das Estratégias de Mapeamento Objeto-Relacional em XML As estratégias de mapeamento objeto-relacional definem o funcionamento essencial da DPL. De forma a tornar flexível a manutenção destas estratégias, encontrase fora do código da DPL, ou seja, estão armazenadas em um arquivo externo do tipo XML, utilizado pela DPL. Este arquivo XML contém basicamente metadados das classes da aplicação, bem como suas associações, e principalmente a correspondência para estruturas do banco de dados relacionais (tabelas e campos). A Listagem 1 demonstra que o arquivo XML está centrado nas informações das classes, seus atributos e associações. E ainda onde estes elementos da orientação a objetos estão mapeados no banco de dados relacional. A estrutura do arquivo XML facilita a leitura das informações por parte da DPL através de um conjunto de tags prédefinidas. A validação desta estrutura pode ainda ser feita com uso de XML Schema, permitindo maior integridade das informações. Listagem 1. Mapeamento XML para a DPL 01: <?xml version="1.0" encoding="iso-8859-1"?> 02: <mapeamento> 03: <classes> 04: <classe> 05: <nomeclasse>Curso</nomeclasse> 06: <heranca></heranca> 07: <tabela>Curso</tabela> 08: <tipo>Concreta</tipo> 09: <atributos> 10: <atributo> 11: <nome>ID</nome> 12: <OID>TRUE</OID> 13: <campo>OID</campo> 14: <tipodado>STRING</tipodado> 15: <tamanho>38</tamanho> 16: </atributo> 17: <atributo> 18: <nome>codigocurso</nome> 19: <OID>FALSE</OID> 20: <campo>cod_curso</campo> 21: <tipodado>INTEGER</tipodado> 22: <tamanho></tamanho> 23: </atributo> 24: </atributos> 25: <associacoes> 26: <classeassociada> 27: <nomeclasse>Disciplina</nomeclasse> 28: <tipoassociacao>agregation</tipoassociacao> 29: <cardinalidade>one</cardinalidade> 30: <atributoinverso>curso</atributoinverso> 31: </classeassociada> 32: <classeassociada> 33: <nomeclasse>Professor</nomeclasse> 34: <tipoassociacao>one-to-one</tipoassociacao> 35: <cardinalidade>one</cardinalidade> 36: <atributoinverso>curso</atributoinverso> 37: </classeassociada> 38: </associacoes> 39: </classe> 40: </classes> 41: </mapeamento> A DPL realiza na fase inicial da aplicação o carregamento das informações do arquivo XML de mapeamento. As informações obtidas são armazenadas em um conjunto de classes que facilitam ainda mais a leitura e navegação pelo mapa de classes da aplicação, além de evitar o acesso constante ao disco rígido, o que afetaria o desempenho da DPL. Este conjunto de classes, por sua vez, faz parte de uma arquitetura implementada pela DPL para facilitar a interação com as aplicações. 4.2 Arquitetura da DPL Existem diversas classes que formam a arquitetura básica da DPL, sendo: OID, TDPLPersistentObject, TDPLPersistenceBroker, TDPLPersistenceMapXML, TDPLRelationalDatabase e TDPLSQLStatement. O diagrama de classes da DPL pode ser visto na Figura 8, onde os atributos e serviços foram suprimidos para facilitar a visualização do modelo. Figura 8. O diagrama de classes da DPL A classe OID é responsável por gerar identificadores únicos para os objetos instanciados. Como tais identificadores podem ser criados a partir de diversas abordagens, a utilização desta classe permite que futuramente a forma de gerar os OIDs possa ser modificada independente do funcionamento da DPL. Atualmente o algoritmo utilizado para criação do OID é baseado no GUID (Globally Unique Identifier). O GUID é representado por um conjunto de caracteres hexadecimais, cuja formação garante a unicidade do identificador gerado [13]. A classe TDPLPersistentObject é uma das classes principais na arquitetura da DPL. Através dela, e somente dela, a aplicação orientada a objetos recebe por herança toda a capacidade de persistência. Esta classe associa-se na arquitetura às outras classes de maneira a permitir que as informações dos objetos sejam persistidas em bancos de dados relacionais, através da geração de expressões SQL, comunicação e envio destas expressões ao banco de dados relacional, dentre outras atividades. A classe TDPLPersistenceBroker é responsável pela interação com o banco de dados relacional bem como tarefas de inicialização da DPL. Desta forma implementa operações que coordenam a carga do mapa XML, a inicialização do banco de dados e envio de expressões SQL ao banco. A classe TDPLPersistenceMapXML é responsável por carregar em memória as informações do arquivo de mapeamento XML, permitindo assim o acesso mais rápido às informações de mapeamento objeto-relacional, além da flexibilidade de mudança no arquivo, que pode ser recarregado novamente caso necessário. A classe TDPLSQLStatement é responsável pela geração de expressões SQL para realização das operações de persistência junto ao banco de dados relacional. Podem ser geradas várias expressões SQL para uma única operação de persistência, de acordo com as definições realizadas no arquivo XML. Ou seja, a persistência de um único objeto pode gerar várias expressões SQL para manutenção em diversas tabelas no modelo relacional. A classe TDPLRelationalDatabase tem como responsabilidade o encapsulamento das funcionalidades do componente DBExpress. Desta forma, esta classe controla a conexão ao banco de dados, bem como os parâmetros inerentes ao driver apropriado do banco, a execução das expressões SQL e desconexão do banco. O funcionamento coordenado das classes acima mencionadas, cada qual com responsabilidades bem definidas, permitindo abstrair da aplicação as tarefas relacionadas à persistência de objetos. 5. Estudo de Caso Como estudo de caso foi adotado o Sistema de Controle Acadêmico (SCA) que é utilizado em algumas disciplinas do curso de Sistemas de Informação da Faculdade Metodista Granbery, sendo um dos motivos pelo qual foi escolhido. O préconhecimento das características do SCA foi um facilitador para a equipe, para poder então se concentrar nos aspectos relativos à construção e implementação da DPL. Porém, para a adequação do Sistema de Controle Acadêmico, visto que o mesmo originalmente foi construído de forma estruturada, foi necessária a realização de uma reengenharia do sistema para torná-lo aderente ao paradigma orientado a objetos, o que permitiu a elaboração de uma documentação completa deste sistema seguindo o padrão IEEE std 830-1998 para especificação de requisitos. 5.1 Desenvolvimento do Sistema de Controle Acadêmico O SCA seguiu algumas fases de desenvolvimento de forma que pudesse estar preparado para utilização com a DPL. Como citado anteriormente, na fase inicial sofreu uma reengenharia com objetivo de torná-lo orientado a objetos e ao mesmo tempo produzir uma documentação padronizada e completa, consistindo da elaboração dos diagramas de casos de uso, de classes e de seqüência, utilizando a simbologia da UML (Unified Modeling Language). O Sistema de Controle Acadêmico foi então desenvolvido em uma única versão capaz de utilizar as diversas estratégias de persistência envolvidas neste trabalho. Por isso, sua implementação contém códigos padronizados e genéricos compatíveis a todas as estratégias abordadas (DPL, frameworks de persistência e banco de dados objetorelacional Caché). Desta forma, e obedecendo ao padrão MVC, foram implementadas as camadas View (formulários e relatórios) e Controller (denominada gerente). Estas duas camadas são únicas para todas as abordagens. Apenas a camada Model foi implementada de acordo com cada abordagem, não interferindo nas demais. A camada Controller implementa gerentes para cada caso de uso, tendo como responsabilidade intermediar as solicitações e envios de objetos entre as camadas Model e View, de forma genérica o bastante para permitir a troca da implementação feita na camada Model, sem necessidade de modificações na camada View. A camada Controller transfere objetos do tipo ClientDataset, criados dinamicamente, em resposta a solicitações e envios de dados dos objetos, permitindo maior independência entre as camadas. Na camada Model foram implementadas as classes da aplicação. No caso da implementação com a DPL foram criadas as classes que herdam de uma superclasse comum chamada TDPLPersistentObject. Esta característica define a camada como sendo intrusiva, pois exige a realização da herança. Porém, o SCA foi preparado não somente para esta abordagem como também para a não intrusiva, como no caso da utilização do banco de dados Caché. A abordagem de uso do padrão MVC não é uma obrigatoriedade para implementação com a DPL, nem mesmo com as demais estratégias. Porém, o desafio de construção deste tipo de aplicação foi um motivador a mais durante o projeto de pesquisa. Sendo assim, o Sistema de Controle Acadêmico foi desenvolvido em camadas separadamente até a implementação dos gerentes para cada caso de uso. Posteriormente, foram implementadas as camadas de modelo para cada abordagem. Sendo então a única diferença entre cada abordagem, a camada Model. 5.2 Implementação do Sistema de Controle Acadêmico com a DPL Como uma das fases iniciais da implementação da DPL no Sistema de Controle Acadêmico, destaca-se a construção do arquivo XML de mapeamento objeto-relacional. De posse da documentação do SCA, em especial do diagrama de classes, foi possível construir o arquivo de mapeamento. Como o banco de dados sofreu alterações mínimas (basicamente inclusão de campos para OIDs) da versão original construída através do paradigma estruturado para a versão orientada a objetos, os diagramas como DER (Diagrama de Entidade e Relacionamentos) e DTR (Diagrama de Tabelas Relacionais) serviram também como base para elaboração do arquivo XML de mapeamento objeto-relacional. O estudo de caso relacionado à implementação com a DPL descrito neste trabalho concentra-se em apenas um fragmento do SCA, tendo sido escolhido o caso de uso Cadastrar Curso, pois possui classes e associações cujas características são apropriadas para o teste das funcionalidades implementadas na DPL. Como o próprio nome já indica, o caso de uso Cadastrar Curso permite a manutenção (pesquisa, inclusão, alteração, exclusão e consulta) de cursos no Sistema de Controle Acadêmico. Na documentação completa do SCA, este caso de uso faz parte dos cenários da Secretaria, que trata de funcionalidades realizadas pela secretaria de uma instituição de ensino. A Figura 9 mostra um fragmento do diagrama de classes do Sistema Acadêmico, considerando apenas as classes relacionadas a este caso de uso. Os atributos foram retirados para facilitar a leitura do modelo. Pessoa Criar() Pers is tir() Excluir() RecuperaObjeto() RecuperaObjetos () Curs o Criar() Pers is tir() Excluir() RecuperaObjeto() RecuperaObjetos () 1 0..* Coordena Profes s or 1 Aloca 0..* Figura 9. Fragmento do diagrama de classes do Sistema Acadêmico A classe Pessoa é uma classe abstrata e possui atributos que são comuns às classes Professor e Aluno. Esta classe, bem como a classe Curso, herda diretamente da classe TDPLPersistentObject recebendo assim a capacidade de persistência oferecida pela DPL. A classe Professor por sua vez recebe esta capacidade de persistência de maneira indireta, ou seja, pela herança de Pessoa. As classes implementam propriedades para cada atributo, sendo esta uma obrigatoriedade para implementação com a DPL. As propriedades substituem os métodos get e set, necessários para modificar dados dos objetos sem desrespeitar o princípio do encapsulamento. Embora não seja necessário, podem-se criar propriedades que acionam métodos get e set caso queira-se respeitar à risca o padrão de uso destes tipos de métodos. As propriedades devem ter o mesmo nome utilizado internamente no arquivo XML de mapeamento para referenciar os campos das tabelas. As classes Professor e Curso implementam os serviços Criar, Excluir, Persistir, RecuperaObjeto e RecuperaObjetos, que acionam serviços correspondentes nos gerentes permitindo a realização das respectivas operações, sendo o serviço RecuperaObjetos responsável pela recuperação de todos os objetos da classe. A classe Curso implementa a associação do tipo um-para-um com Professor, adicionando um atributo cujo tipo refere-se a este último, da mesma forma como especificado no arquivo XML. Ao identificar o tipo de dado do atributo como sendo uma classe, a DPL verifica se esta faz parte da hierarquia de TDPLPersistentObject. Caso isto seja verdadeiro, o objeto relacionado ao atributo é instanciado automaticamente e recebe o OID correspondente. Observa-se que somente o OID é recuperado, pois é o suficiente para identificar o objeto e posteriormente pode ser recuperado por completo. Um único gerente é instanciado no momento da execução de cada operação e este instância um objeto da classe Curso obtendo ou transferindo a ele os dados para persistência. Tal transferência acontece de maneira transparente ao desenvolvedor da aplicação. Os dados da interface são alocados em um ClientDataSet o qual é entregue ao gerente e solicitado à persistência. Ou, é solicitado ao gerente a recuperação dos dados, que são recebidos em um ClientDataSet. Desta forma a programação da interface é simplificada de forma que o desenvolvedor não necessite conhecer como e onde os dados são persistidos. O caso de uso Cadastrar Curso, bem como todos os demais, foi especificado em termos de seu Fluxo Principal, Alternativos e de Exceções, além de sub-fluxos representando as operações básicas de inclusão, alteração, exclusão e consulta. A interface principal deste caso de uso considerando o sub-fluxo de inclusão pode ser vista na Figura 10. Figura 10. Interface Principal do Caso de Uso Cadastrar Curso 6. Considerações Finais A persistência de objetos é um tema importante quando se fala em desenvolvimento de sistemas orientados a objetos. As dificuldades atuais inerentes à utilização de bancos de dados orientados a objetos levam a busca de alternativas para a realização da persistência. Devido à grande inserção no mercado, os bancos de dados relacionais são uma possível alternativa. Ao determinar a utilização do modelo relacional para persistência de objetos, deve-se levar em consideração a diferença existente entre estes dois paradigmas, orientado a objetos e relacional. O mapeamento objeto-relacional apresenta diversas estratégias para reduzir a diferença entre os dois paradigmas, a partir do mapeamento de classes, atributos e associações em tabelas, campos e relacionamentos. As estratégias não apresentam solução única para cada caso, mas devem levar em consideração características do modelo de classes, questões como manutenibilidade e desempenho. A construção de camadas de persistência, embora não seja trivial, mostra-se possível, através da DPL, e podem ser obtidos resultados visíveis com a sua implementação. O domínio de temas como engenharia de software, padrões de projeto e orientação a objetos, entre outros, mostra-se extremamente necessário para acomodar o processo de desenvolvimento de um framework de persistência. Agradecimentos À Faculdade Metodista Granbery pelo apoio dado ao projeto de iniciação científica Estratégias de Persistência em Software Orientado a Objetos, no qual este trabalho está inserido. Referencias Bibliográficas [1] [2] [3] [4] [5] [6] ALMEIDA, V. M. G. Persistência de Objetos no Banco de Dados Caché. Trabalho de Conclusão de Curso, Bacharelado em Sistemas de Informação – Faculdade Metodista Granbery, Juiz de Fora, 2005. ALMEIDA, V. M. G.; JULIO, A. M. O.; ARAÚJO, M. A. P. Persistência de Objetos no Caché. SQL Magazine, Rio de Janeiro, v. 22, p. 16-21, 2005. ALMEIDA, V. M. G.; JULIO, A. M. O.; ARAÚJO, M. A. P. Implementando um Projeto no Caché. SQL Magazine, Rio de Janeiro, v. 25, p. 8-14, 2005. ALMEIDA, V. M. G.; JULIO, A. M. O.; ARAÚJO, M. A. P. Desenvolvendo Aplicações Web no Caché. SQL Magazine, Rio de Janeiro, v. 29, p. 60-62, 2005. SILVA, J. C.; ARAÚJO, M. A. P.; JULIO, A. M. O. Delphi X Caché Persistindo Objetos no Banco de Dados Caché. Active Delphi, v. 28, p. 10-16, junho, 2006. DAIBERT, M. S. Persistência em Software Orientado a Objetos: Soluções de Mapeamento Objeto-Relacional. Trabalho de Conclusão de Curso, [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] Bacharelado em Sistemas de Informação – Faculdade Metodista Granbery, Juiz de Fora, 2005. DAIBERT, M. S.; JULIO, A. M. O.; ARAÚJO, M. A. P. Persistência em Software Orientado a Objetos: Abordagens Utilizando Frameworks Opensource. In: II FESTSOL - Festival de Software Livre, Juiz de Fora, 2005. DAIBERT, M. S.; JULIO, A. M. O.; ARAÚJO, M. A. P. Persistência em Software Orientado a Objetos: Abordagens Utilizando Frameworks Opensource. In: III Encontro de Software Livre do Amazonas (ESLAM), Manaus, 2005. DAIBERT, M. S.; JULIO, A. M. O.; ARAÚJO, M. A. P. Persistência de Objetos no Delphi: Introdução ao Framework DePO (Delphi Persistent Objects). Active Delphi, v. 24, p. 16 - 21, fevereiro, 2006. DAIBERT, M. S.; JULIO, A. M. O.; ARAÚJO, M. A. P. Persistência de Objetos no Delphi: Introdução ao Framework IO (Instant Objects). Active Delphi, v. 25, p. 30 - 34, março, 2006. SILVA, J. C. Camada de Persistência Objeto-Relacional para Delphi. 2005, Trabalho de Conclusão de Curso, Bacharelado em Sistemas de Informação – Faculdade Metodista Granbery, Juiz de Fora, 2005 AMBLER, S. W. Mapping Object to Relational Databases. 1999. Disponível em: <http://www.ambysoft.com/mappingObjects.pdf>. Acesso em: 08 jul. 2006 AMBLER, S. W. The Design of a Robust Persistence Layer for Relational Databases. Nov. 2000. Disponível em: < http://www.ambysoft.com/essays/persistenceLayer.html >. Acesso em: 01 jul. 2006. KELLER, W. Mapping Objects to Tables - A Pattern Language. 1997, 2004. Disponível em: <http://www.objectarchitects.de>. Acesso em: 08. jul. 2006. KELLER, W. Persistence Options for Object-Oriented Programs. 2004. Disponível em: <http://www.objectarchitects.de>. Acesso em: 08. jul. 2006. YODER, J. W., JOHNSON, R. E., WILSON, Q. D. Connecting Business Objects to Relational Databases. In Proceedings of the 5th Conference on the Pattern Languages of Programs, Monticello, 1998. Disponível em: <http://www.joeyoder.com/Research/objectmappings/Persista.pdf>. Acesso em: 08. jul. 2006.