universidade do vale do itajaÍ centro de ...siaibib01.univali.br/pdf/francis benito odisi.pdfapi...
TRANSCRIPT
UNIVERSIDADE DO VALE DO ITAJAÍ CENTRO DE CIÊNCIAS TECNOLÓGICAS DA TERRA E DO MAR
CURSO DE CIÊNCIA DA COMPUTAÇÃO
GERAÇÃO DE CÓDIGO PARA ACESSO A DADOS UTILIZANDO OS PADRÕES MVC E DAO
Área de Engenharia de Software
por
Francis Benito Odisi
Fabiane Barreto Vavassori Benitti, Drª. Eng. Orientadora
Itajaí (SC), novembro de 2008
UNIVERSIDADE DO VALE DO ITAJAÍ CENTRO DE CIÊNCIAS TECNOLÓGICAS DA TERRA E DO MAR
CURSO DE CIÊNCIA DA COMPUTAÇÃO
GERAÇÃO DE CÓDIGO PARA ACESSO A DADOS UTILIZANDO OS PADRÕES MVC E DAO
Área de Engenharia de Software
por
Francis Benito Odisi Relatório apresentado à Banca Examinadora do Trabalho de Conclusão do Curso de Ciência da Computação para análise e aprovação. Orientadora: Fabiane Barreto Vavassori Benitti.
Itajaí (SC), novembro de 2008
ii
SUMÁRIO
LISTA DE ABREVIATURAS...................................................................v
LISTA DE FIGURAS................................................................................vi
RESUMO..................................................................................................viii
ABSTRACT................................................................................................ ix
1 INTRODUÇÃO....................................................................................10 1.1 PROBLEMATIZAÇÃO ................................................................................... 12 1.1.1 Formulação do Problema............................................................................... 12 1.1.2 Solução Proposta............................................................................................. 13 1.2 OBJETIVOS ...................................................................................................... 15 1.2.1 Objetivo Geral ................................................................................................. 15 1.2.2 Objetivos Específicos ...................................................................................... 15 1.3 METODOLOGIA.............................................................................................. 15 1.4 ESTRUTURA DO TRABALHO ..................................................................... 18
2 FUNDAMENTAÇÃO TEÓRICA......................................................19 2.1 PADRÕES DE PROJETO................................................................................ 19 2.1.1 Model-View-Controller (MVC) ..................................................................... 21 2.1.2 Data Access Object (DAO)............................................................................. 29 2.2 GERAÇÃO DE CÓDIGO ................................................................................ 35 2.2.1 Vantagens e Desvantagens ............................................................................. 38 2.2.2 Preocupações Sobre a Geração de Código ................................................... 39 2.2.3 Formas de Geração de Código Ativa ............................................................ 40 2.2.4 Geração de Código para a Camada de Acesso a Dados.............................. 45 2.2.5 NVelocity.......................................................................................................... 46 2.3 ACESSO A METADADOS DE UM BANCO DE DADOS........................... 49 2.3.1 Estrutura.......................................................................................................... 50 2.3.2 Recuperando Metadados com a API MyMeta............................................. 53 2.4 FERRAMENTAS SIMILARES....................................................................... 56 2.4.1 Ferramentas Comerciais ................................................................................57 2.4.2 Ferramentas Open-Source ............................................................................. 59 2.4.3 Ferramentas Acadêmicas ............................................................................... 66 2.4.4 Análise Comparativa ...................................................................................... 68
3 PROJETO.............................................................................................74 3.1 DEFINIÇÃO DOS REQUISITOS E REGRAS DE NEGÓCIOS ................ 74 3.2 DIAGRAMA DE CASOS DE USO ................................................................. 78 3.2.1 Cenários e Protótipos de Tela ........................................................................ 79 3.3 DIAGRAMA DE CLASSES............................................................................. 79 3.4 XML SCHEMA ................................................................................................. 83
iii
3.5 PADRÕES DE PROJETOS UTILIZADOS................................................... 90
4 DESENVOLVIMENTO DA FERRAMENTA .................................95 4.1 IMPLEMENTAÇÃO DA FERRAMENTA........................ ............................ 95 4.1.1 Recuperação dos Metadados dos Bancos de Dados..................................... 95 4.1.2 Opções de Configurações ...............................................................................96 4.1.3 Geração de Código-Fonte...............................................................................97 4.1.4 Persistência do Projeto ................................................................................... 99 4.1.5 Testes de Unidade ......................................................................................... 100 4.2 FERRAMENTA PROPOSTA - KEOPS....................................................... 102 4.2.1 Funcionamento da Ferramenta ................................................................... 102 4.2.2 Demonstração da Ferramenta ..................................................................... 103
5 CONSIDERAÇÕES FINAIS ............................................................120 5.1 TRABALHOS FUTUROS.............................................................................. 122
REFERÊNCIAS BIBLIOGRÁFICAS .................................................124 A CENÁRIOS E PROTÓTIPOS DE TELA DOS CASOS DE USO131 B CÓDIGO-FONTE DO XML SCHEMA..........................................148 C TEMPLATES C# ...............................................................................152 C.1 DTO TEMPLATE........................................................................................... 152 C.2 DAO TEMPLATE........................................................................................... 152
D TEMPLATES VB.NET .....................................................................156 D.1 DTO TEMPLATE........................................................................................... 156 D.2 DAO TEMPLATE........................................................................................... 156
E CÓDIGO-FONTE C# ........................................................................160 E.1 PESSOA............................................................................................................ 160 E.2 ALUNO............................................................................................................. 160 E.3 PROFESSOR ................................................................................................... 160 E.4 TCC................................................................................................................... 161 E.5 PESSOADAO................................................................................................... 161 E.6 ALUNODAO.................................................................................................... 162 E.7 PROFESSORDAO ..........................................................................................162 E.8 TCCDAO.......................................................................................................... 163
F CÓDIGO-FONTE VB.NET ..............................................................167 F.1 PESSOA............................................................................................................ 167 F.2 ALUNO............................................................................................................. 167 F.3 PROFESSOR ................................................................................................... 167 F.4 TCC................................................................................................................... 168 F.5 PESSOADAO................................................................................................... 168 F.6 ALUNODAO.................................................................................................... 169 F.7 PROFESSORDAO ..........................................................................................169
iv
F.8 TCCDAO.......................................................................................................... 170
v
LISTA DE ABREVIATURAS
ADO ActiveX Data Objects API Application Program Interface ASP Active Server Pages BSD Berkeley Software Distribution COM Component Object Model DAO Data Access Object DBMS Database Management System HTML HyperText Markup Language IDE Integrated Development Environment JDBC Java Database Connectivity J2EE Java 2 Enterprise Edition MDA Model Driven Architecture MFC Microsoft Foundation Classes MSBUILD Microsoft Build Engine MVC Model View Controller MVCE Model View Controller Editor ODBC Open Database Connectivity OLE DB Object Linking and Embedding Database PHP Hypertext Preprocessor RAD Rapid Application Development RF Requisitos Funcionais RN Regras de Negócio RNF Requisitos Não Funcionais SQL Structured Query Language TCC Trabalho de Conclusão de Curso TMVE Thing Model View Editor UNIVALI Universidade do Vale do Itajaí UML Unified Modeling Language VTL Velocity Template Language W3C Word Wide Web Consortium XMI XML Metadata Interchange XML Extensible Markup Language XSD XML Schema Definition
vi
LISTA DE FIGURAS
Figura 1. Processo de geração de código-fonte da ferramenta proposta............................................14 Figura 2. Modelo de colaboração implementado na solução Smalltalk-80 .......................................23 Figura 3. Diagrama de classes do padrão MVC implementado no Smalltalk-80 ..............................23 Figura 4. Estrutura do padrão MVC utilizando o padrão Observer...................................................25 Figura 5. Diagrama de classes UML do padrão Observer.................................................................26 Figura 6. Diagrama de classes UML do padrão Observer.................................................................26 Figura 7. Diagrama de classes do padrão Strategy.............................................................................28 Figura 8. Diagrama de classes do padrão DAO .................................................................................30 Figura 9. Diagrama de seqüência do padrão DAO para consulta a dados .........................................31 Figura 10. Diagrama de seqüência do padrão DAO para inserção, alteração e exclusão de dados ...32 Figura 11. Diagrama de classes do padrão DAO utilizando o padrão Abstract Factory...................35 Figura 12. Ciclo básico do processo de geração de código................................................................37 Figura 13. Fluxo de geração de código do code munger...................................................................41 Figura 14. Fluxo de geração de código do inline-code expander......................................................42 Figura 15. Fluxo de geração de código do mixed-code generator.....................................................43 Figura 16. Fluxo de entrada e saída de dados de um partial-class generator....................................44 Figura 17. Processo de geração de código de um partial-class generator para uma camada de acesso
a dados........................................................................................................................................44 Figura 18. Processo de geração de código do tier or layer generator para uma camada de acesso a
dados...........................................................................................................................................45 Figura 19. Código utilizado para mapear os valores de entrada e as referências do template...........47 Figura 20. Exemplo de arquivo template escrito em VTL.................................................................48 Figura 21. Utilização do motor de templates NVelocity....................................................................49 Figura 22. Resultado gerado pelo merge............................................................................................49 Figura 23. Diagrama de classes da API MyMeta...............................................................................51 Figura 24. Conexão ao banco de dados SQL Server através da API MyMeta ..................................54 Figura 25. Conexão ao banco de dados Oracle através da API MyMeta...........................................54 Figura 26. Código escrito em C# para retornar informações de tabelas ............................................55 Figura 27. Código escrito em C# para retornar informações de visões .............................................55 Figura 28. Código escrito em C# para retornar informações de procedimentos e funções................56 Figura 29. Código template utilizado pelo Code Smith para listar tabelas de um banco de dados ...57 Figura 30. Código template utilizado pelo TaHoGen criar os getters e setters de uma classe ..........59 Figura 31. Código-fonte gerado a partir do template exibido na Figura 26.......................................60 Figura 32. Utilização da classe PropertyGenerator...........................................................................61 Figura 33. Exemplo de código-fonte da classe TheProperties...........................................................62 Figura 34. Interface gerada para definir os valores das propriedades da classe TheProperties.........62 Figura 35. Exemplo de arquivo template utilizado pelo Smart Code Generator ...............................63 Figura 36. Exemplo de código-fonte gerado a partir da utilização de template.................................63 Figura 37. Interface de usuário do Smart Code Generator para recuperação de metadados..............64 Figura 38. Código-fonte gerado a partir dos metadados de tabelas de um banco de dados...............65 Figura 39. Arquitetura interna do Cordel ...........................................................................................67 Figura 40. Arquitetura do XSpeed .....................................................................................................68 Figura 41. Diagrama de casos de uso da ferramenta proposta ...........................................................78 Figura 42. Diagrama de classes da ferramenta proposta....................................................................81 Figura 43. Visão geral do XML Schema da ferramenta proposta......................................................85 Figura 44. Estrutura do elemento ConfigurationSettings...................................................................86
vii
Figura 45. Estrutura do elemento Table.............................................................................................86 Figura 46. Estrutura do elemento Field..............................................................................................87 Figura 47. Estrutura do elemento Class.............................................................................................88 Figura 48. Estrutura do elemento Attribute........................................................................................88 Figura 49. Estrutura do elemento UnderlyingDBObjects..................................................................88 Figura 50. Estrutura do elemento Method..........................................................................................89 Figura 51. Estrutura do elemento Template.......................................................................................90 Figura 52. Diagrama de classes UML representando a implementação das principais classes .........92 Figura 53. Diagrama de classes UML representando o mecanismo de validação e notificação de
violações nas regras de negócio .................................................................................................93 Figura 54. Código-fonte necessário para recuperação de metadados dos bancos de dados suportados
....................................................................................................................................................96 Figura 55. Código-fonte necessário para validação das regras de negócio........................................97 Figura 56. Código-fonte necessário para geração do código-fonte....................................................98 Figura 57. Arquivo template em linguagem VTL utilizado para a geração de código-fonte ............99 Figura 58. Código-fonte utilizado para persistir as configurações realizadas no projeto ................100 Figura 59. Lista dos testes de unidade utilizados para validação da ferramenta e o resultado da
execução de tais testes..............................................................................................................102 Figura 60. Diagrama de atividades para a ferramenta Keops ..........................................................103 Figura 61. Modelo de entidade e relacionamento do estudo de caso...............................................104 Figura 62. Comandos SQL para criação das tabelas do estudo de caso...........................................105 Figura 63. Tela para a criação de um novo projeto..........................................................................105 Figura 64. Mapeamento entre tipos do banco de dados e da linguagem de programação ...............106 Figura 65. Configuração dos mapeamentos de tipos padrão............................................................106 Figura 66. Configurações padrões para geração de código-fonte ....................................................107 Figura 67. Configuração dos dados para conexão com o banco de dados .......................................107 Figura 68. Tabelas existentes no banco de dados do estudo de caso ...............................................108 Figura 69. Criação de classe base a partir de duas ou mais tabelas do banco de dados...................109 Figura 70. Lista de objetos do projeto contendo as tabelas do banco de dados e as classes geradas
..................................................................................................................................................110 Figura 71. Configuração da classe Tcc ............................................................................................110 Figura 72. Configuração de método de classe..................................................................................112 Figura 73. Configuração dos arquivos templates a serem utilizados durante o processo de geração
..................................................................................................................................................113 Figura 74. Geração de código-fonte das classes do projeto .............................................................114 Figura 75. Diagram de classes das classes geradas pela ferramenta Keops.....................................115 Figura 76. Resultado da compilação do código-fonte gerado..........................................................116 Figura 77. Interface de validação do código-fonte gerado...............................................................117 Figura 78. Código-fonte para cadastrar um TCC.............................................................................118 Figura 79. Interface para abrir um projeto existente ........................................................................133 Figura 80. Interface para configurar as classes que devem ser criadas a partir da decomposição de
uma tabela ................................................................................................................................139 Figura 81. Interface que valida estrutura de atributos de classe com estrutura de colunas da tabela
..................................................................................................................................................144
viii
RESUMO
ODISI, Francis Benito. Geração de código para acesso a dados utilizando os padrões MVC e DAO. Itajaí, 2008. 160 f. Trabalho de Conclusão de Curso (Graduação em Ciência da Computação)–Centro de Ciências Tecnológicas da Terra e do Mar, Universidade do Vale do Itajaí, Itajaí, 2008. O processo de desenvolvimento de software está evoluindo constantemente. Cresce a cobrança pelo aumento de produção em um menor espaço de tempo, mantendo a qualidade dos artefatos de software produzidos. Para que as equipes de desenvolvimento possam atender tais necessidades é necessário que o ambiente de desenvolvimento forneça ferramentas capazes de apoiar a produção de software, como ferramentas que automatizem a geração de artefatos de software. A utilização de padrões específicos para cada etapa do ciclo de desenvolvimento ajuda a garantir a qualidade do produto final, pois um padrão fornece a solução para um problema específico em um determinado contexto. Durante a etapa de codificação podem ser adotados alguns padrões de projeto que incrementam a facilidade de manutenção e extensão do software. Pode ser citado o padrão MVC (Model-View-Controller), que permite separar uma aplicação em três camadas: (i) Modelo; (ii) Visão; e (iii) Controle. Esta separação permite que a codificação ocorra separadamente, aumentando a flexibilidade e reutilização de código. Outro padrão que pode ser utilizado é o DAO (Data Access Object), que abstrai das classes de negócio os detalhes de acesso ao mecanismo de persistência. Em problemas de larga escala cuja arquitetura é bem definida, como na criação de camadas de acesso a dados, aconselha-se a utilização de geração de código. Para gerar a camada de acesso a dados, pode ser efetuada a leitura dos metadados de um banco de dados existente, e gerar as classes de acesso aos dados com base em tais metadados. Gerar código-fonte que utilize padrões de projeto, permite criar de maneira automatizada códigos robustos, padronizados e flexíveis, facilitando a manutenção do software. O objetivo deste trabalho é construir uma ferramenta de geração de código para a camada Modelo do MVC baseado nas tabelas existentes de um banco de dados. O código-fonte pode ser gerado para qualquer linguagem de programação baseada em texto devido à utilização de arquivos templates para formatar a saída gerada. A ferramenta proposta, denominada Keops, foi desenvolvida na linguagem de programação C#, suportando os bancos de dados Oracle, PostgreSQL e SQL Server, utilizando a API (Application Program Interface) MyMeta para leitura de metadados e o motor de templates NVelocity durante o processo de geração de código. A persistência das configurações realizadas pelo usuário na ferramenta foi realizada em um arquivo XML ( Extensible Markup Language). Espera-se que este trabalho auxilie no processo de desenvolvimento de aplicativos que necessitem efetuar a persistência de informações em um banco de dados. Palavras-chave: Padrões de Projeto. Geração de Código-Fonte. MVC / DAO.
ix
ABSTRACT
The software development process is in constant evolution. The demand of software production in less time keeping the quality in software artefacts that are produced is growing. To fulfill these needs, the development team needs an enviroment that supplies tools which are able to support the software production, as the ones that automate the generation of software artefacts. The use of specific patterns for each development cycle phase helps guaranteeing the quality of the final product, because a pattern describes the core of the solution to a specific problem that occurs over a specific context. During the codification phase, some design patterns that improve the software maintenance facility and extension can be adopted. The MVC (Model-View-Controller) pattern, wich separates an application in three layers, model, view, controller, can be cited as example. This separation improves the flexibility and code reuse, because it allows each layer to be created separately from the other ones. Another pattern known as DAO (Data Access Object), wich encapsulates data access details from business layer, can also be used. In well-known large-scale problems, as data access layers, the use of code generation is advised. The classes of data access layer can be generated based on existing database table’s metadata. Generating code that uses design patterns allows creating flexible, standardized and robust code in an automated way, turning software maintenance easier. The objective of this work is to build a code generation tool for MVC Model layer, based on existing database tables. The source-code could be generated for any text-base programming language, because of using template files to format the generated output. This proposed tool, named Keops, was developed in C# programming language, using MyMeta API (Application Program Interface) for reading metadata information from Oracle, PostgreSQL and SQL Server databases, and NVelocity template engine during the code generation process. The configuration made by the user was saved in a XML (Extensible Markup Language) file. It’s hoped that this project aims the development process of applications that need to effectuate the information persistence in a database. Keywords: Design Patterns. Code Generation. MVC / DAO.
1 INTRODUÇÃO
O processo de desenvolvimento de software está em constante evolução. Cada vez mais se
espera que haja aumento da produção dentro de um espaço de tempo mais curto, sem deixar de lado
a qualidade do artefato de software produzido. Sommerville (2007) cita que para que seja possível
desenvolver aplicações de maneira rápida, o ambiente de desenvolvimento deve possuir ferramentas
poderosas capazes de apoiar a produção de software, como linguagens de programação de bancos
de dados e ferramentas que automatizem a geração de artefatos1 de software. Além disso, a
padronização dos processos ajuda a garantir a qualidade dos artefatos de software produzidos.
Em cada etapa de desenvolvimento é possível a inclusão de padrões2 que especificam boas
práticas que devem ser adotadas para a execução das tarefas correspondentes a cada fase do
processo. Considera-se que os padrões descrevem a solução para um problema comum em um
contexto específico (GAMMA et al, 1995, tradução nossa). Magela (2006) lembra ainda que para
cada fase do desenvolvimento são adotados padrões específicos como para a área de negócios,
análise ou projeto de sistemas.
Segundo Magela (2006), a utilização de padrões nos projetos aumenta a qualidade do
produto final devido à garantia de atendimento aos requisitos ao qual o padrão se propõe a resolver.
A utilização de padrões incrementa a facilidade de manutenção e extensão do software, pois o
conhecimento do problema facilita a compreensão do código (SHALLOWAY; TROTT, 2004,
tradução nossa).
Um dos padrões utilizados com freqüência para desenvolvimento de sistemas é o MVC
(Model-View-Controller). Ele apresenta uma arquitetura em três camadas: a camada Modelo
(Model) representa os dados da aplicação, a camada de Interface (View) representa a interface com
o usuário e a camada Controle (Controller) especifica a maneira que a interface reagirá conforme as
entradas do usuário. Sua utilização permite que a codificação ocorra separadamente, aumentando a
flexibilidade e reutilização de código (GAMMA et al, 1995, tradução nossa). No entanto, a 1Artefato: Informação produzida, modificada ou utilizada por um processo, define uma área de responsabilidade, e está sujeita a controle de versão. Um artefato pode ser um modelo, um elemento de um modelo ou um documento (KROLL; KRUCHTEN, 2003, tradução nossa). De acordo com Rumbaugh, Jacobson e Booch (2004, tradução nossa), artefato é uma informação utilizada ou produzida por um processo de desenvolvimento de software. Pode ser um modelo, uma descrição ou software. 2 O termo padrões vem do inglês patterns.
11
utilização do MVC incrementa também a complexidade do projeto da aplicação, pois introduz
codificação adicional que permite a separação das camadas (SUNMICROSYSTEMS, 2002a,
tradução nossa). Logo, o tempo necessário para desenvolver a primeira aplicação utilizando MVC,
tende a ser maior devido à quantidade extra de código exigida. No entanto, este esforço é
compensado com o desenvolvimento de outras aplicações, pois é possível reutilizar o código já
escrito.
A camada de Modelo, em especial, pode interagir com outro padrão denominado DAO
(Data Access Object). O uso do DAO permite abstrair e encapsular todos os acessos à fonte de
dados, sendo responsabilidade do mesmo gerenciar as conexões, armazenar e recuperar dados da
fonte de dados (SUNMICROSYSTEMS, 2002b, tradução nossa). Sua utilização permite que os
objetos de negócio possam acessar as informações persistidas sem conhecer os detalhes específicos
de implementação da fonte de dados. Além disso, uma possível migração de banco de dados torna-
se muito mais simples, pois como os objetos de negócio não possuem conhecimento sobre o modelo
de persistência, a migração requer alterações apenas nos objetos de acesso a dados (SUN
MICROSYSTEMS, 2002b, tradução nossa).
A adoção dos padrões MVC e DAO permite que sejam construídos códigos reutilizáveis,
flexíveis e robustos. No entanto, com o aumento da quantidade de codificação a ser implementada,
aumenta também a complexidade e o tempo necessários. Por tal motivo, uma solução a ser utilizada
durante a etapa de desenvolvimento da aplicação é a utilização de uma ferramenta para geração de
código.
A geração de código tem como objetivo criar todo ou parte de outros programas. A
utilização de complexos frameworks como Microsoft .NET, MFC (Microsoft Foundation Classes),
J2EE (Java 2 Enterprise Edition), entre outros, associado às boas práticas de programação através
da utilização de padrões, resulta em códigos robustos, reutilizáveis, e complexos. Neste caso, a
utilização de ferramentas para a geração de código pode contribuir muito no ciclo de
desenvolvimento de software. Geralmente, quanto maior a complexidade do framework, mais
atrativo torna-se a utilização de técnicas de geração de código (HERRINGTON, 2003b, tradução
nossa).
A geração de código é útil e aconselhável para problemas de grande escala como a
construção de camadas de acesso à bancos de dados, stored procedures, comandos SQL (Structured
Query Language), ou qualquer outro problema bem conhecido cuja arquitetura possua uma
12
estabilidade bem definida. Quando a estabilidade do código gerado é duvidosa ou quando não se
tem certeza sobre o projeto e/ou arquitetura da aplicação, a geração de código automatizada não é
aconselhável (HERRINGTON, 2003b, tradução nossa).
O objetivo da ferramenta proposta é possibilitar a geração de código que construa as classes
de acesso a um banco de dados da camada Modelo do MVC utilizando o DAO, baseado em
metadados das tabelas de um banco de dados já existente. Busca-se com este trabalho auxiliar o
processo de desenvolvimento de aplicativos que possuam a necessidade de persistir informações em
um banco de dados, gerando código para qualquer linguagem de programação baseada em texto
através da utilização de templates.
Pelo fato do código gerado não estar vinculado a apenas uma linguagem de programação ou
framework específico, há maiores chances de que ela possa ser realmente aplicada no processo de
desenvolvimento de software.
1.1 PROBLEMATIZAÇÃO
1.1.1 Formulação do Problema
Antes da popularização dos computadores pessoais, o desenvolvimento de software era caro
e demorado. Os recursos disponíveis para a criação de programas, ferramentas de desenvolvimento
e linguagens de programação, possuiam funcionalidades limitadas e apresentavam uma
complexidade considerável.
No entanto, tais ferramentas e linguagens de programação evoluíram. Foram criadas
linguagens de alto nível que abstraem dos programadores detalhes específicos do hardware utilizado
para executar a aplicação. Além disso, novas funcionalidades foram acrescentadas nas ferramentas
de desenvolvimento. A necessidade de persistir informações, sejam elas em bancos de dados,
arquivos XML (Extensible Markup Language), arquivos texto, ou qualquer outra forma de
persistência, tornou-se comum e está presente na maioria das aplicações.
Além disso, espera-se que os softwares sejam criados da maneira mais rápida possível, sem
afetar a qualidade dos artefatos produzidos. Em cada etapa do processo de desenvolvimento de
software podem ser adotados padrões específicos que determinam boas práticas a serem seguidas
durante a execução das tarefas (MAGELA, 2006). Durante a etapa de codificação podem ser
adotados padrões de projeto, que ajudam na criação de códigos capazes de solucionar um problema
13
específico, sem comprometer a manutenção e extensão do código-fonte (SHALLOWAY; TROTT,
2004, tradução nossa).
Tornou-se comum também a busca pela reutilização e padronização do código-fonte criado.
A padronização permite que os desenvolvedores compreendam de forma mais rápida qual o
objetivo do código-fonte, mesmo que o programador nunca tenha visto tal código. De certa forma é
difícil padronizar um código-fonte, pois cada programador geralmente possui um estilo próprio de
codificação. Novamente os padrões de projeto podem auxiliar na solução deste problema, pois
definem diretrizes que devem ser seguidas durante a codificação. Mas apenas padrões de projeto
não são suficientes para garantir a padronização do código-fonte. A padronização do código-fonte
pode ser adquirida a medida que o código-fonte é gerado de forma automática, ou seja, através de
uma ferramenta de geração de código. Portanto, a associação entre padrões de projeto e técnicas de
geração de código ajuda consideravelmente na criação rápida de código-fonte padronizado.
Desta forma, uma ferramenta de geração de código-fonte que permita gerar código-fonte
seguindo padrões como o MVC e o DAO, por exemplo, seria de grande utilidade na etapa de
codificação de software, pois permitiria a criação automatizada de códigos padronizados, flexíveis e
reutilizáveis, facilitando a manutenção e extensão do código-fonte.
1.1.2 Solução Proposta
A proposta deste trabalho é desenvolver uma ferramenta de geração de código-fonte para as
classes de acesso ao banco de dados da camada Modelo do padrão MVC em conjunto com o padrão
DAO. O código-fonte pode ser gerado em qualquer linguagem de programação baseada em texto
devido à utilização de arquivos templates para formatar o arquivo de código-fonte de saída. Para
gerar as classes de acesso ao banco de dados, o sistema busca informações nos metadados das
tabelas de um banco de dados já existente e com base em tais metadados permite que o usuário
configure as classes a serem geradas.
Algumas ferramentas similares já existem. No entanto a maioria delas apresenta limitações
relacionadas à possibilidade de utilização de templates ou de acesso a um banco de dados existente
para extração dos metadados. Portanto, uma ferramenta que consiga gerar código para várias
linguagens baseado nos metadados de tabelas de um banco de dados existente, seria de grande
utilidade no processo de desenvolvimento de software.
14
A ferramenta proposta possui basicamente três grandes funcionalidades. A primeira delas é a
recuperação dos metadados de um banco de dados existente, sendo suportados os sistemas
gerenciadores de bancos de dados Oracle, PostgreSQL e SQL Server. A segunda grande
funcionalidade é a configuração das classes a serem geradas. Tendo como base os metadados
recuperados, o usuário pode selecionar quais as classes que devem ser geradas, bem como
configurar os atributos e métodos a serem gerados. Além disso, o usuário pode selecionar os
arquivos templates que devem ser utilizados durante o processo de geração de código-fonte. O
objetivo de tais arquivos é configurar os arquivos de saída, ou seja, o código-fonte, permitindo que
seja gerado código-fonte para qualquer linguagem de programação baseada em texto. A terceira e
última grande funcionalidade da ferramenta é a geração do código-fonte em si. Através desta
funcionalidade o código-fonte será gerado, levando em consideração as configurações realizadas
pelo usuário e os arquivos templates selecionados. A Figura 1 mostra o processo de geração de
código da ferramenta proposta.
Figura 1. Processo de geração de código-fonte da ferramenta proposta
A ferramenta foi criada para a plataforma .NET, utilizando o Microsoft .NET Framework
2.0 e a linguagem de programação C#. As configurações realizadas pelo usuário foram persistidas
em XML. O motor de templates utilizado é o NVelocity e a leitura dos metadados foi realizada com
a API (Application Program Interface) MyMeta.
15
1.2 OBJETIVOS
1.2.1 Objetivo Geral
Construir uma ferramenta de geração de código para a camada Modelo do padrão MVC
baseado nas tabelas existentes de um banco de dados.
1.2.2 Objetivos Específicos
• Pesquisar e descrever as características do processo de geração de código;
• Pesquisar e descrever os padrões MVC e DAO e a integração da camada Modelo do
MVC com o DAO;
• Permitir a geração de classes de acesso a um banco de dados da camada Modelo do
MVC em conjunto com o padrão DAO, com base nos metadados de tabelas de um banco
de dados já existente. O banco de dados utilizado como base para geração de código
poderá ser tanto o Oracle quanto o Microsoft SQL Server, ou ainda o PostgreSQL3; e
• Permitir a utilização de templates para formatação do arquivo de código gerado.
1.3 METODOLOGIA
A proposta deste trabalho é construir uma ferramenta capaz de gerar o código-fonte das
classes de acesso a um banco de dados da camada Modelo do padrão MVC, em conjunto com o
padrão DAO para encapsular o acesso ao mecanismo de persistência utilizado pela aplicação. O
código-fonte pode ser gerado para qualquer linguagem de programação baseada em texto, devido a
utilização de arquivos templates que tem como objetivo definir o formato do arquivo de saída, ou
seja, o código-fonte.
Na primeira etapa da proposta, foi fundamental a aquisição do conhecimento teórico sobre
os temas abordados neste trabalho, sendo tal conhecimento descrito no Capítulo 2 deste documento.
Neste capítulo são abordados quatro grandes temas: (i) padrões de projeto; (ii) geração de código-
3 Os bancos de dados suportados pela aplicação foram escolhidos seguindos os seguintes critérios: (i) Oracle e SQL Server estarem entre os três bancos de dados comerciais mais utilizados no mundo, com mercado de 47,1% e 17,4%, respectivamente (GARTNER, 2007, tradução nossa); e (ii) PostgreSQL ser um dos banco de dados open-source que apresenta um grande número de funcionalidades semelhantes aos bancos de dados Oracle e SQL Server (MCALLISTER, 2008, tradução nossa).
16
fonte; (iii) acesso a metadados de bancos de dados; e (iv) ferramentas similares. Para que o
conhecimento teórico pudesse ser adquirido, foram efetuadas leituras de diversos livros, artigos,
revistas, anais de eventos, monografias e documentos eletrônicos publicados na Internet.
Com base em tais fontes, foi possível descrever as características e vantagens em utilizar
padrões de projeto, primeiro tema abordado no Capítulo 2. Foram enfatizados os padrões MVC e
DAO, a relação entre estes dois padrões, bem como a relação existente entre outros padrões de
projetos com o MVC e DAO. Desta forma, foi possível conhecer algumas das possíveis variações
de implementação dos padrões citados. Buscou-se efetuar a pesquisa em obras de autores
conceituados na área de padrões de projeto, como Gamma et al (1995) que são conhecidos por seu
pioneirismo nesta área, Fowler (2002), Buschmann (1996), Buschmann, Henney e Schmidt (2007) e
nas obras originais de Reenskaug, criador do padrão MVC que podem ser vistas em Reenskaug
(1979a), Reenskaug (1979b) e Reenskaug (2003).
Na seção que aborda o tema geração de código, foram levantadas as vantagens,
desvantagens, técnicas e preocupações relacionadas a geração de código. Também foi realizado um
estudo sobre a geração de código para acesso a dados, ou seja, para manipular informações de
algum mecanismo de persistência, dando ênfase em banco de dados. Para a compreensão deste tema
foram essenciais as obras dos pioneiros nesta área, Jack Herrington e J. Craig Cleaveland, que
podem ser observadas em Herrington (2003a), Herrington (2003a) e Cleaveland (1988).
Outro tema essencial para a continuidade desta proposta, leitura de metadados de um banco
de dados, também é abordado no Capítulo 2. Foi estudada a API MyMeta, que efetua a leitura dos
metadados de doze diferentes bancos de dados, entre eles os suportados pela ferramenta proposta.
Foi documentada a estrutura de classes desta API, além de um exemplo de código-fonte mostrando
como efetuar a leitura dos metadados.
O último tema abordado no Capítulo 2 refere-se às ferramentas similares existentes. Foi
realizado um estudo envolvendo quatorze ferramentas similares. Foi efetuado o download da
maioria das ferramentas, para que fosse possível avaliar as funcionalidades das ferramentas
similares e verificar se havia alguma lacuna a ser explorada. As ferramentas foram categorizadas
em ferramentas comerciais, ferramentas open-source e ferramentas acadêmicas. Por último, foi
efetuada uma análise comparativa entre as ferramentas similares estudadas.
17
Além da fundamentação teórica, encontra-se no Capítulo 3 deste documento o projeto da
ferramenta proposta. Neste projeto foram descritos todos os requisitos funcionais, requisitos não
funcionais e regras de negócio identificadas para a ferramenta proposta. Além do levantamento de
requisitos, foi elaborado o Diagrama de Casos de Uso e a descrição dos cenários de cada caso de
uso, inicialmente realizado em conjunto com a prototipação de telas e, posteriormente ajustado para
a versão final da ferramenta. Com o intuito de detalhar a arquitetura da ferramenta proposta, foi
criado um diagrama de classes, que contém as principais classes da aplicação proposta. Para a
modelagem do Diagrama de Casos de Uso e Diagrama de Classe foi utilizada a ferramenta
Enterprise Architect e para a modelagem dos protótipos e desenvolvimento da ferramenta foi
utilizado o Microsoft Visual Studio 2005.
Pelo fato de permitir que as configurações realizadas pelo usuário sejam salvas em um
arquivo XML para posterior leitura, foi descrito de maneira visual o XML Schema que descreve o
formato do arquivo XML utilizado para persistir as configurações realizadas pelo usuário. Para a
criação do arquivo XML Schema foi utilizada a ferramenta Liquid XML Studio.
Ainda no Capítulo 3, aborda-se os padrões de projeto utilizados durante o desenvolvimento
da ferramenta. Entre eles, destacam-se o padrão MVC, DAO, Data Transfer Object e Business
Object. Para demonstrar o uso dos padrões citados anteriormente, bem como de outros utilizados
durante o desenvolvimento foram criados diagramas de classes que demonstram como ocorreu a
aplicação dos padrões de projeto na ferramenta proposta.
Após a documentação do projeto, foi descrito no Capítulo 4 o desenvolvimento da
ferramenta. Este capítulo descreve como ocorreu a implementação da ferramenta, estando dividido
em quatro etapas: (i) recuperação dos metadados dos bancos de dados; (ii) opções de configurações;
(iii) geração de código-fonte; e (iv) persistência do projeto. Em seguida, foram documentados os
testes unidade utilizado durante a etapa de desenvolvimento, que auxiliaram a garantir o correto
funcionamento de algumas das principais unidades de código implementadas.
Por último, foram abordadas as características da ferramenta desenvolvida. Para tal, foi
criado um diagrama de atividades que demonstra os passos necessários para a utilização da
ferramenta. Para desenvolver este diagrama foi utilizada a ferramenta Entreprise Architect. Em
seguida, tendo como objetivo validar a ferramenta desenvolvida é descrito, através de uma série de
passos, um estudo de caso que cuja abordagem possibilitou validar todas as funcionalidades da
ferramenta.
18
1.4 ESTRUTURA DO TRABALHO
Este trabalho está estruturado em cinco capítulos: Introdução, Fundamentação Teórica,
Projeto, Desenvolvimento e Considerações Finais.
O primeiro capítulo deste trabalho aborda uma introdução sobre o assunto, a descrição do
problema e a proposta de solução do mesmo, bem como os objetivos a serem atingidos até a
finalização deste trabalho.
O segundo capítulo apresenta uma análise aprofundada sobre os temas abordados no
trabalho. O primeiro assunto fala sobre os padrões de projeto, dando ênfase nos padrões MVC e
DAO. Em seguida é abordada a geração de código-fonte, suas vantagens, desvantagens, técnicas de
geração de código-fonte, bem como a geração de código-fonte para acesso a dados. A recuperação
de informações dos metadados de um banco de dados existente também é apresentada neste
capítulo. Portanto, comenta-se sobre a API MyMeta, que pode ser utilizada para tal objetivo,
demonstrando sua estrutura de classes e um exemplo de código-fonte que pode ser utilizado para
recuperar os metadados de um banco de dados. Por último, abordam-se as ferramentas similares
existentes, agrupadas em três categorias denominadas ferramentas comerciais, ferramentas open-
source e ferramentas acadêmicas, realizando-se uma análise comparativa entre as ferramentas
apresentadas e a ferramenta proposta.
No terceiro capítulo são abordados os requisitos funcionais e não-funcionais da aplicação,
bem como as regras de negócio do sistema proposto. Em seguida, são apresentados e detalhados os
casos de uso identificados, além do diagrama de classes da ferramenta proposta. Para detalhar a
estrutura do arquivo XML utilizado para persistir as configurações realizadas pelo usuário, é
descrito o XML Schema utilizado pela aplicação. Por último, são apresentados os padrões de
projeto utilizados durante o desenvolvimento da ferramenta proposta.
O quarto capítulo objetiva apresentar o desenvolvimento da ferramenta proposta. Para tal foi
descrito o funcionamento da ferramenta, bem como elaborado e apresentado um estudo de caso que
possibilitou validar as funcionalidades da ferramenta, descritas nos requisitos funcionais e regras de
negócio.
Por fim, o quinto capítulo, e último, apresenta as considerações finais sobre o trabalho
desenvolvido, bem como um conjunto de sugestões para trabalhos futuros, que objetivam direcionar
possíveis interessados em tornar a ferramenta mais robusta.
19
2 FUNDAMENTAÇÃO TEÓRICA
A fundamentação teórica do presente trabalho aborda os seguintes temas: (i) padrões de
projeto, que descreve de maneira geral os padrões de projeto, dando ênfase nos padrões MVC e
DAO, suas vantagens, desvantagens, formas de implementação, entre outros; (ii) geração de código,
que comenta sobre as vantagens e desvantagens da geração de código, técnicas de geração, além de
detalhar a geração de código para acesso a dados; (iii) acesso a metadados de um banco de dados,
que descreve como pode ser realizado a acesso aos metadados dos bancos de dados suportados pela
ferramenta proposta, além de demonstrar um código-fonte de exemplo que realiza tal tarefa; e (iv)
descrição das ferramentas semelhantes, que mostra as características das ferramentas similares já
existentes, além de realizar uma análise comparativa entre tais ferramentas e a ferramenta proposta.
2.1 PADRÕES DE PROJETO
O termo padrão foi definido por Christopher Alexander em um de seus livros nomeado A
Pattern Language: Towns, Buildings, Constrution, 1977 (METSKER, 2002, tradução nossa), e
especifica que:
...cada padrão descreve um problema que ocorre várias vezes em nosso ambiente, e então descreve a base para a solução deste problema, de maneira a permitir que esta solução seja reutilizada milhões de vezes, sem a necessidade de refazê-la (ALEXANDER, 1977 apud GAMMA et al, 1995, tradução nossa).
Cada padrão tem o objetivo de resolver um problema específico, sendo que a identificação e
documentação de um novo padrão, também conhecida como catalogação, deve ser realizada, de
acordo com Magela (2006), por um modelador experiente, que conheça os detalhes e passos
necessários para a resolução do problema, bem como outros padrões que possam se relacionar com
o novo padrão catalogado. Gamma et al (1995, tradução nossa) descreve que a documentação de um
padrão deve ter no mínimo quatro elementos:
• Nome: O nome deve ser usado para descrever um problema de projeto, sua solução e
conseqüências em uma ou duas palavras;
• Descrição do problema: A descrição do problema especifica quando o padrão deve ser
utilizado, podendo conter uma lista de condições que obrigatoriamente devem ser
satisfeita para aplicação do padrão;
20
• Descrição da solução: A solução descreve os elementos que formam o projeto, seus
relacionamentos, responsabilidades e colaborações. Deve fornecer uma descrição
abstrata do problema e como a combinação dos elementos propostos soluciona-o; e
• Conseqüência: A conseqüência deve descrever os resultados, vantagens e desvantagens
em utilizar tal padrão. Como a reutilização de código é um importante fator em projetos
orientados a objeto, deve ser descrito o impacto que a implementação do padrão causa
sobre a flexibilidade, extensibilidade e portabilidade do sistema.
No entanto, o nível de detalhamento de um padrão varia consideravelmente de acordo com a
situação. Em certos casos, uma breve descrição do problema e da solução é o suficiente para uma
boa catalogação. Já em outros casos, pode ser necessária uma documentação detalhada contendo
diagramas e exemplos de código-fonte (BUSCHMANN; HENNEY; SCHMIDT, 2007, tradução
nossa).
Em cada etapa de desenvolvimento podem ser adotados conjuntos de padrões específicos
como os Business Patterns, Analysis Patterns e Design Patterns, utilizados durante as fases de
análise de negócios, análise de sistemas e implementação de sistemas, respectivamente (MAGELA,
2006). Kerievsky (2004, tradução nossa), Buschmann, Henney e Schmidt (2007, tradução nossa) e
Gamma et al (1995, tradução nossa) descrevem alguma vantagens da utilização de padrões:
• Ajuda a remover a duplicação de código;
• Simplifica a lógica;
• Incrementa a flexibilidade do sistema;
• Permite a implementação de soluções simples, com elegância e versatilidade;
• Permite a reutilização de projetos4 e arquiteturas;
• Torna a codificação mais acessível para novos desenvolvedores do sistema;
• Melhoram a documentação e manutenção dos sistemas existentes por fornecer uma
especificação explícita das interações entre classes e objetos e a intenção de uso dos
mesmos;
4 O termo projeto vem do inglês design.
21
• Ajuda o projetista a encontrar a solução adequada de maneira mais rápida; e
• Auxilia na escolha de projetos que tornam o sistema reutilizável, evitando escolhas que
comprometam a reusabilidade do modelo escolhido.
Cada padrão ajuda a resolver um problema específico, mas nem todo problema possui um
padrão definido (MAGELA, 2006). Muitas vezes, uma solução simples é capaz de resolver o
problema, sem deixar de lado a extensibilidade, portabilidade e elegância do código. Kerievsky
(2004, tradução nossa) acrescenta que alguns programadores tendem a utilizar padrões, sem a real
necessidade de fazê-lo, apenas para adquirir experiência implementando-os ou talvez para serem
reconhecidos por escreverem códigos realmente bons, e complexos. Logo, o conhecimento sobre os
problemas que um padrão pode realmente ajudar a solucionar é a melhor maneira para decidir
quando utilizá-lo.
É comum que na catalogação dos padrões sejam apresentados exemplos de codificação ou
diagramas que demonstrem sua estrutura. Buschmann, Henney e Schmidt (2007, tradução nossa) e
Kerievsky (2004, tradução nossa) lembram que é também comum que muitos programadores vejam
o exemplo e o diagrama fornecidos na catalogação como sendo a única maneira de implementar ou
representar a estrutura do padrão. No entanto, é normal haverem variações na implementação e
estrutura de um mesmo padrão, sendo que, a melhor implementação a ser utilizada é aquela que
melhor atende as necessidades do problema em questão, desde que não altere a base da solução
proposta pelo padrão.
2.1.1 Model-View-Controller (MVC)
A possibilidade de reutilização de código foi e continua sendo uma grande vantagem para os
desenvolvedores de software. Contudo, esta característica nem sempre esteve presente no mundo da
programação de computadores. A possibilidade de demonstrar os mesmos dados de maneiras
totalmente diferentes ou possuir os mesmos dados e a mesma lógica de negócio em sistemas
diferentes, exigia a recodificação de toda a funcionalidade.
Com base neste problema, Trygve Reenskaug em seu escrito de 12 de maio de 1979, sugeriu
o padrão TMVE (Thing-Model-View-Editor), que viria a ser o predecessor do atual padrão MVC
(Model-View-Controller), que conforme cita Booch et al (2007, tradução nossa), é um padrão
amplamente utilizado. Nesta proposta inicial, de acordo com Reenskaug (1979a, tradução nossa), a
Coisa (Thing) representa algo de interesse do usuário; o Modelo (Model) define a representação de
22
uma abstração na forma de dados; a Visão (View) define uma ou mais representações visuais de um
dado Modelo; o Editor (Editor) serve como interface entre o usuário e uma ou mais Visões,
fornecendo ao usuário os comandos necessários para interagir com a Visão.
Em 10 de dezembro de 1979, Trygve Reenskaug propôs uma evolução do padrão anterior,
denominado MVCE (Model-View-Controller-Editor). Conforme Reenskaug (1979b, tradução
nossa), nesta evolução o Modelo (Model) é definido como um objeto ou estrutura de objetos que
representam o conhecimento; a Visão (View) é a representação visual de um Modelo; o Controle
(Controller) é a conexão entre o usuário e o sistema, recebendo as entradas do usuário e traduzindo-
as em mensagens a serem passadas adiante para uma ou mais Visões; o Editor (Editor) é um
Controle especial, que fica entre a camada Visão e a camada Controle, permitindo ao usuário
modificar as informações apresentadas pela Visão, sendo removido e descartado após o término do
processo de edição.
Segundo Magela (2006), as idéias de Reenskaug foram implementadas por Jim Althoff, após
a saída de Reenskaug da Xerox PARC, no Smalltalk-80 Class Library e é reconhecidamente o atual
padrão MVC. A implementação realizada por Althoff apresenta diferenças do modelo proposto por
Reenskaug, sendo que na idéia original, o Controle equivale ao que se conhece atualmente por
ferramenta5, não sendo implementado por Althoff. Já o elemento Controle do atual padrão MVC
corresponde ao elemento Editor proposto por Reenskaug.
O elemento Editor, na proposta inicial do TMDE, era responsável por tratar a entrada e saída
de dados (REENSKAUG, 2003, tradução nossa). A implementação realizada por Althoff manteve a
camada Modelo responsável pelos dados e concentrou-se no problema de separar a entrada e saída
de dados, dividindo as atribuições do Editor para dois elementos: a Visão e o Controle (MAGELA,
2006). A Visão passou a ser responsável pela apresentação, ou seja, saída de dados, enquanto o
Controle tornou-se responsável por receber e interpretar as entradas de dados dos usuários
(REENSKAUG, 2003, tradução nossa). A Figura 2 e a Figura 3 exibem o relacionamento com a
idéia de colaboração presente no Smalltalk-80 e o diagrama de classes UML (Unified Modeling
Language) do padrão MVC implementado no Smalltalk-80, respectivamente.
5 O termo ferramenta vêm do inglês Tool.
23
Figura 2. Modelo de colaboração implementado na solução Smalltalk-80
Fonte: Adaptado de Reenskaug (2003).
Figura 3. Diagrama de classes do padrão MVC implementado no Smalltalk-80
Fonte: Reenskaug (2003).
De acordo com Metsker (2002, tradução nossa) e Fowler (2002, tradução nossa), o maior
valor do MVC é a separação entre o Modelo e o resto da aplicação, deixando-o em seu próprio
24
domínio de aplicação. Quando esta separação ocorre, é possível criar camadas de código, abrindo a
possibilidade de executar diferentes camadas em diferentes computadores. Logo, o MVC oferece
suporte para sistemas multicamadas, trazendo vantagens no desenvolvimento e distribuição da
aplicação. Fowler (2002, tradução nossa), Larman (2004, tradução nossa) e Reenskaug (2003,
tradução nossa) mostram algumas vantagens em separar o Modelo e a Visão:
• Permite que novas interfaces sejam facilmente conectadas a Modelos existentes, sem
afetar o Modelo em si;
• Permite fácil portabilidade do Modelo para outras interfaces;
• Permite que a codificação do Modelo e da interface seja realizada separadamente;
• Permite que um mesmo Modelo seja visualizado de diferentes maneiras; e
• Permite testar toda a camada Modelo sem a necessidade de complicadas ferramentas
baseadas em script que realizam testes em interfaces de usuário.
Com a separação das camadas Modelo e Visão, os objetos da camada Modelo, que conforme
lembra Freeman et al (2004, tradução nossa), são responsáveis pela lógica de negócio e pelos dados,
não devem saber sobre a existência dos objetos da camada Visão (LARMAN 2004, tradução nossa).
No entanto, ao ocorrer alguma atualização nos dados dos objetos da camada Modelo, o objeto ou
objetos da camada Visão ligados ao objeto Modelo devem ser notificados sobre a alteração, para
que realizem a atualização dos dados para o usuário (BROEMMER, 2003, tradução nossa).
É consenso na literatura que os padrões Observer e Strategy auxiliam no atendimento dos
princípios do MVC durante sua implementação, e portanto, tais padrões são explicados na seção
abaixo. Tal consenso pode ser observado em Larman (2004, tradução nossa), Fowler (2002,
tradução nossa), Gamma et al (1995, tradução nossa), Buschmann et al (1996, tradução nossa) e
Buschmann, Henney e Schmidt (2007, tradução nossa).
2.1.1.1 Observer e Strategy
É extremamente importante que a notificação sobre a atualização nos dados dos objetos da
camada Modelo enviada para o objeto ou objetos da camada Visão seja realizada sem criar uma
dependência entre tais camadas, pois do contrário, o maior princípio do MVC simplesmente
deixaria de existir. Para que isso se torne possível pode-se adotar o padrão conhecido como
Observer (LARMAN, 2004, tradução nossa; FOWLER, 2002, tradução nossa; GAMMA et al,
25
1995, tradução nossa; TROWBRIDGE et al, 2003, tradução nossa). A Figura 4 mostra a estrutura
do padrão MVC utilizando o padrão Observer.
Figura 4. Estrutura do padrão MVC utilizando o padrão Observer
Fonte: Trowbridge et al (2003).
De acordo com Gamma et al (1995, tradução nossa), o padrão Observer define uma
dependência entre objetos de um para muitos, de maneira que quando um objeto altera seu estado,
todos os dependentes são notificados e alterados automaticamente. Gross (2006, tradução nossa)
menciona que este mecanismo de notificação é semelhante ao processo conhecido como
Publish/Subscribe. O objeto que deve notificar sobre a alteração de seu estado é denominado
subject, enquanto que o objeto que recebe as notificações de alteração de estado do subject é
denominado observer (BISHOP, 2008, tradução nossa).
O padrão Observer permite que o subject fique separado de seus dependentes, os observers,
possibilitando ao mesmo, funcionalidade completa, independentemente da existência ou não de
dependentes (HOHPE; WOOLF, 2003, tradução nossa). A Figura 5 e a Figura 6 mostram duas
representações do diagrama de classes UML do padrão Observer, reforçando a idéia de Buschmann,
Henney e Schmidt (2007, tradução nossa), Kerievsky (2004, tradução nossa), entre outros, que
afirmam que há diferentes maneiras de implementar o mesmo padrão.
26
Figura 5. Diagrama de classes UML do padrão Observer
Fonte: Gamma et al (1995).
Figura 6. Diagrama de classes UML do padrão Observer
Fonte: Bishop (2008).
Hohpe e Woolf (2003, tradução nossa) e Bishop (2008, tradução nossa), sugerem duas
maneiras de passar o novo estado do objeto subject para seus dependentes, conhecidas como
modelo push e modelo pull.
No modelo push, o novo estado do subject é passado para cada um dos observers por
parâmetro no método Update. Este modelo evita que cada um dos dependentes interessados na
mudança de estado do subject tenha que solicitar o novo estado através do método GetState.
Entretanto, dados desnecessários são passados para observers não interessados na mudança de
estado do subject.
27
No modelo pull, o objeto subject envia a notificação para seus dependentes com a mínima
quantidade de informação necessária, através do método Update, sendo de responsabilidade de cada
observer solicitar o novo estado ao subject, através do método GetState, conforme diagrama
apresentado na Figura 5. Neste modelo, cada observer pode solicitar apenas informações que
realmente deseja saber, mas o subject deverá ser capaz de responder múltiplas solicitações para os
mesmos dados.
Fowler (2002, tradução nossa) e Buschmann, Henney e Schmidt (2007, tradução nossa),
citam que ao utilizar o padrão Observer para notificar a camada Visão sobre a alteração de estado
do Modelo, os objetos da camada Visão atuam como observers do objeto da camada Modelo, o
subject.
Conforme dito anteriormente, o maior valor do MVC é a separação que ele cria entre a
camada Visão e a camada Modelo, mas nem por isso o Controle deixa de ser fundamental no padrão
MVC. Cabe a ele, receber e traduzir as entradas de dados em solicitações para as camadas Modelo e
Visão (FOWLER, 2002, tradução nossa).
Uma visão pode ter um ou mais objetos Controle, dependendo do mecanismo de resposta
desejado. Caso seja necessária uma Visão do tipo somente-leitura, pode ser utilizado um Controle
que ignore os eventos gerados pela entrada de dados do usuário. Para permitir a alteração dos dados,
tudo que deve ser feito é alterar o Controle ligado a Visão (BUSCHMANN et al, 1996, tradução
nossa). Gamma et al (1995, tradução nossa) afirma que este comportamento pode ser adquirido
através da implementação do padrão Strategy, onde o Controle é o objeto Strategy da Visão, que
gerencia a entrada de dados conforme o contexto desejado.
De acordo com Bishop (2008, tradução nossa) e Gamma et al (1995, tradução nossa), o
padrão Strategy tem por objetivo definir uma família de algoritmos, encapsulá-los e torná-los
acessíveis, através de um objeto denominado Contexto, permitindo que o cliente escolha qual
algoritmo deseja utilizar. Para que isso seja feito, são criadas várias classes que implementam
diferentes comportamentos e compartilham a mesma interface (LARMAN, 2004, tradução nossa).
Cooper (2003, tradução nossa) lembra ainda que, em alguns casos, o Contexto pode escolher o
algoritmo mais apropriado para um cliente. A Figura 7 mostra o diagrama de classes UML do
padrão Strategy.
28
Figura 7. Diagrama de classes do padrão Strategy
Fonte: Gamma et al (1995).
Assim como a Visão solicita o recebimento de notificações sobre as alterações do Modelo,
Buschmann et al (1996, tradução nossa) menciona que, caso o comportamento do Controle dependa
das alterações ocorridas no Modelo, o mesmo pode solicitar também o recebimento de tais
notificações, assim como faz a Visão. No entanto, Fowler (2002, tradução nossa) afirma que, na
prática, a maioria dos sistemas possui apenas um Controle por Visão, havendo até casos em que
existe apenas um Controle para toda a aplicação.
2.1.1.2 MVC: padrão composto por padrões
O padrão MVC, que segundo Magela (2006) também é conhecido por arquitetura, aplicação
ou framework MVC, auxilia no desenvolvimento de aplicações constituídas por três camadas,
denominadas Modelo, Visão e Controle. Esta divisão permite reutilizar o código já existente, bem
como representar os mesmos dados de diferentes formas de maneira simples, além de outras
vantagens já citadas anteriormente. Fowler (2002, tradução nossa) cita ainda que os princípios do
MVC podem ser aplicados de várias maneiras.
Larman (2004, tradução nossa), Fowler (2002, tradução nossa), Gamma et al (1995,
tradução nossa), entre outros, sugerem que seja utilizado o padrão Observer para realizar a
notificação de alteração do estado do Modelo para a camada Visão. De acordo com Buschmann et
al (1996, tradução nossa), uma visão pode ter um ou mais objetos Controle, que variam conforme o
comportamento desejado, permitindo a utilização do padrão Strategy.
Gamma et al (1995, tradução nossa) lembra ainda que o padrão MVC pode utilizar o padrão
Composite para lidar com Visões compostas, o padrão Decorator para adicionar funcionalidades
adicionais às Visões, como por exemplo, uma barra de rolagem, e o padrão Factory Method para
29
especificar uma classe Controle padrão para uma Visão. Buschmann, Henney e Schmidt (2007,
tradução nossa) mencionam que a utilização conjunta de padrões de projeto auxilia na construção de
arquiteturas flexíveis e reutilizáveis, enfatizando que o MVC é um padrão de projeto composto, pois
possibilita em sua arquitetura, a utilização de outros diversos padrões.
2.1.2 Data Access Object (DAO)
De acordo com Fowler (2002, tradução nossa), o padrão DAO (Data Access Object) é uma
especialização de um padrão denominado Gateway. Trowbridge et al (2004, tradução nossa),
observam que o padrão Gateway elimina a necessidade de diferentes sistemas para compreender
como se conectar a um recurso externo, fornecendo uma interface única de acesso. Nock (2003,
tradução nossa), lembra ainda que o padrão DAO também é conhecido como Data Accessor.
Logo, o padrão DAO é utilizado para encapsular e centralizar o acesso a uma fonte de dados
(SINGH et al, 2002, tradução nossa). Este acesso pode ser a um banco de dados relacional ou
orientado a objetos, mainframes, sistemas legados, flat-files ou qualquer outro repositório de
arquivos (ALUR; CRUPI; MALKS, 2003, tradução nossa).
Buschmann, Henney e Schmidt (2007, tradução nossa) citam que o uso do padrão DAO
permite a separação entre a lógica de negócio e a manipulação e acesso aos dados persistidos. Com
isso, criam-se duas camadas denominadas Camada de Negócio e Camada de Integração. A Camada
de Negócios é formada pelos objetos de negócios e é responsável por conter toda a lógica de
negócios do sistema, enquanto que a Camada de Integração contém todos os objetos DAO e é
responsável por gerenciar a comunicação com recursos não-locais e sistemas externos, como bancos
de dados, sistemas legados, repositórios de dados, entre outros.
2.1.2.1 Estrutura
De acordo com Wolff, Schmidt e Völter (2001, tradução nossa), o objeto DAO deve buscar
e definir os valores dos atributos do objeto de negócio, sendo que, a maneira mais simples de
implementar esta funcionalidade é permitir que o objeto DAO acesse diretamente os atributos do
objeto de negócio. No entanto, esta implementação cria uma enorme dependência entre o objeto de
negócio e o objeto DAO, dificultando testar o objeto DAO separadamente do objeto de negócio.
Para contornar este problema de dependência entre os objetos DAO e o de negócio, Alur,
Crupi e Malks (2003, tradução nossa) e Crawford e Kaplan (2003, tradução nossa), sugerem a
30
utilização de um objeto intermediário, denominado Data Transfer Object, ou apenas Data Object,
ou ainda Transfer Object. A utilização deste objeto permite a troca de dados entre os objetos DAO e
o de negócio, sem que um vínculo direto seja estabelecido entre os mesmos. A Figura 8 mostra o
diagrama de classes proposto por Alur, Crupi e Malks (2003) para o padrão DAO, utilizando um
Data Transfer Object para troca de informações entre os objetos DAO e o de negócio.
Figura 8. Diagrama de classes do padrão DAO
Fonte: Alur, Crupi e Malks (2003).
A interação entre os vários objetos do padrão DAO pode ser visualizada no diagrama de
seqüência mostrado na Figura 9 e na Figura 10. A Figura 9 mostra a interação dos objetos,
conforme modelo proposto por Alur, Crupi e Malks (2003) (Figura 8), para consulta a dados. Já a
Figura 10 representa a interação para inserção, alteração e exclusão de dados.
31
Figura 9. Diagrama de seqüência do padrão DAO para consulta a dados
Fonte: Alur, Crupi e Malks (2003).
32
Figura 10. Diagrama de seqüência do padrão DAO para inserção, alteração e exclusão de dados
Fonte: Alur, Crupi e Malks (2003).
2.1.2.2 Vantagens e Desvantagens
A utilização do padrão DAO, conforme já descrito anteriormente, permite encapsular e
centralizar o acesso a uma fonte de dados (SINGH et al, 2002, tradução nossa). Além desta, outras
vantagens em utilizar este padrão são descritas abaixo e podem ser observadas em Alur, Crupi,
Malks (2003, tradução nossa), Wolff, Schmidt e Völter (2001, tradução nossa), Nock (2003,
tradução nossa) e Marinescu (2002, tradução nossa):
33
• Permite organizar a lógica de acesso a dados e encapsular características específicas para
facilitar a portabilidade e facilidade de manutenção;
• Diminui a complexidade dos objetos de negócio, pois passam a conter apenas a lógica
do negócio, delegando ao objeto DAO as operações relacionadas à persistência;
• Facilita a adoção de um novo tipo de banco de dados para a aplicação, já que a única
alteração a ser realizada refere-se aos objetos DAO;
• Permite desenvolver e testar toda a camada responsável pela persistência dos dados
separadamente do resto da aplicação;
• Facilita a incorporação de estratégias de otimização de performance, pois o acesso a
banco de dados é um gargalo comum na performance de sistemas e geralmente, simples
otimizações garantem um grande efeito no desempenho da aplicação. Como o acesso ao
banco de dados está centralizado nos objetos DAO, é mais fácil identificar e corrigir o
problema, além de aplicar a otimização para todas as partes do sistema que a utilizam,
realizando uma única alteração; e
• Permite abstrair do programador os detalhes do mecanismo de persistência utilizado na
aplicação.
No entanto, mesmo possuindo vantagens marcantes, o padrão DAO também apresenta
desvantagens que são descritas abaixo e podem ser observadas em Wolff, Schmidt e Völter (2001,
tradução nossa) e Nock (2003, tradução nossa):
• Limita o controle de acesso a dados: Como todas as operações lógicas de acesso a dados
estão definidas nos objetos DAO, toda a aplicação fica restrita a tais operações. Logo, se
o DAO for mal-projetado ou não for suficientemente versátil, não atenderá os requisitos
da aplicação; e
• Pequena redução de performance: Como um novo nível de comunicação entre os objetos
foi criado, obtém-se uma pequena redução na performance da aplicação. No entanto,
esta redução é mínima quando comparada a comunicação de processos entre cliente e
servidor, ou até mesmo ao acesso a um banco de dados. Logo, esta redução geralmente
não é percebida na performance do sistema.
34
2.1.2.3 Padrões relacionados: Factory Method e Abstract Factory
Para aplicações simples, onde se sabe que o tipo de fonte de dados não será alterado, a
implementação pura do padrão DAO, sem a utilização de outros padrões de projeto, se faz
suficiente. No entanto, com o aumento da complexidade da aplicação e do número de objetos DAO
criados pela aplicação, ou ainda quando não se sabe exatamente, durante as etapas de projeto e
codificação, qual o objeto DAO que será utilizado, torna-se difícil implementar o padrão DAO sem
o auxílio de outros padrões.
Para solucionar este problema, Stelting e Maassen (2001, tradução nossa) e Alur, Crupi,
Malks (2003, tradução nossa), sugerem a utilização do padrão Factory Method. De acordo com
Gamma et al (1995, tradução nossa), este padrão define uma interface para a criação de objetos,
permitindo ao objeto cliente decidir qual classe deseja instanciar. Alur, Crupi e Malks (2003,
tradução nossa), observam que este padrão pode ser utilizado para produzir os vários tipos de
objetos DAO necessários para a aplicação. Com isso, a criação destes objetos fica centralizada, não
sendo necessário invocar o construtor das classes DAO para instanciar um novo objeto, mas apenas
invocar o método desejado, fornecido pelo objeto responsável por criar os objetos DAO, conforme a
especificação do padrão Factory Method.
Além disso, é comum em grandes aplicações a necessidade de mudar freqüentemente ou de
utilizar diferentes tipos de fontes de dados para persistir os dados. Logo, a aplicação deve estar
preparada para acessar as diversas fontes de dados através da implementação de vários objetos
DAO, um para cada tipo de mecanismo de persistência utilizado pela aplicação. Além disso, cada
objeto de negócio deverá ser capaz de selecionar qual o objeto DAO que deverá ser utilizado. Este
processo de escolha, certamente se tornará repetitivo e trabalhoso, pois cada objeto de negócio
deverá realizar o mesmo processo de seleção.
De acordo com Stelting e Maassen (2001, tradução nossa), Alur, Crupi, Malks (2003,
tradução nossa) e Crawford e Kaplan (2003, tradução nossa), este problema pode ser solucionado
com a implementação do padrão Abstract Factory. Conforme Gamma et al (1995, tradução nossa),
o padrão Abstract Factory fornece uma interface para criar uma família de objetos relacionados ou
dependentes, sem especificar suas classes concretas. Freeman et al (2004, tradução nossa) menciona
que o uso deste padrão permite que um cliente utilize esta interface abstrata para criar um conjunto
de objetos relacionados sem saber, ou se importar, sobre os objetos concretos que são produzidos.
35
Logo, o padrão Factory Method aplicado ao padrão DAO, abstrai dos objetos de negócio o
processo de escolha do mecanismo de persistência apropriado, e portanto, deverão interagir apenas
com a interface fornecida, sem se preocupar com qual objeto DAO está sendo realmente criado
(CRAWFORD; KAPLAN, 2003, tradução nossa). A Figura 11 mostra o diagrama de classes do
padrão DAO utilizando o padrão Abstract Factory.
Figura 11. Diagrama de classes do padrão DAO utilizando o padrão Abstract Factory
Fonte: Crawford e Kaplan (2003).
Portanto, o padrão DAO utiliza o padrão Factory Method para centralizar a criação de
objetos DAO e permitir que o tipo de objeto DAO a ser criado seja escolhido em tempo de
execução e o padrão Abstract Factory para fornecer suporte a diferentes mecanismos de
persistência (ALUR; CRUPI; MALKS, 2003, tradução nossa). Vale lembrar ainda, que não há a
necessidade de utilizar estes padrões para implementar o padrão DAO, a menos que as condições
descritas anteriormente se tornem presentes na aplicação.
2.2 GERAÇÃO DE CÓDIGO
Conforme Herrington (2003a, tradução nossa), a técnica de geração de código refere-se a
utilizar um programa, o gerador de código, para gerar outro programa, ou parte dele. Um gerador de
código é como qualquer outro programa que recebe alguma entrada de dados e cria alguma coisa
nova como saída. Sells (2001, tradução nossa) cita que os conhecidos wizards, comuns em
ferramentas de programação, são exemplos de geradores de código. Além do código-fonte
propriamente dito, a técnica de geração de código também pode ser utilizada para gerar os testes e a
documentação técnica do código-fonte gerado (RUTHERFORD; WOLF, 2003, tradução nossa;
36
FRANCA; STAA, 2002, tradução nossa). Mangano (2005, tradução nossa) observa que grande
parte do progresso no processo de desenvolvimento de software está relacionada à geração de
código, tendo como base uma especificação de alto nível.
Os componentes-chaves de um gerador de código são a lógica do gerador e o código
template. A lógica do gerador define qual código deve ser gerado, enquanto que os templates
produzem o código baseado nos dados fornecidos pela lógica do gerador. Isto permite manter o
porquê e o quando separados do como. Desta maneira, quanto melhor o arquivo template utilizado
melhor o código gerado, possibilitando alterar totalmente a estrutura do código gerado realizando
alterações apenas nos arquivos templates. No entanto, nada impede que um gerador de código seja
construído sem ter a característica de utilizar arquivos templates (HERRINGTON, 2003a, tradução
nossa).
Os geradores de código recebem como entrada uma especificação de alto nível e convertem
tal especificação em programas, ou parte de programas. Esta especificação pode ser definida através
de um diagrama gráfico, através de um formulário interativo onde o usuário seleciona as opções
desejadas, ou até mesmo serem escritas em alguma linguagem, como se fossem uma linguagem de
programação. Após a definição das especificações, o gerador de código atua de maneira semelhante
a um compilador, transformando as informações de alto nível, as especificações realizadas, em uma
implementação de baixo nível, neste caso o código-fonte (CLEAVELAND, 1988, tradução nossa).
A Figura 12 mostra o ciclo básico do processo de geração de código.
37
Figura 12. Ciclo básico do processo de geração de código
Fonte: Adaptado de Cleaveland (1988).
Os geradores de código podem apresentar inúmeras variações. Eles podem gerar desde
alguns simples scripts até aplicações completas; podem possuir uma interface amigável ou trabalhar
através de linha de comando; podem ser capazes de gerar código para apenas uma única linguagem
de programação ou para várias; podem ser capazes de gerar o código uma única vez, ou múltiplas
vezes; podem apresentar diferentes tipos de entrada e saída de dados. Enfim, podem variar
consideravelmente. No entanto, o único conceito imutável é que o código-fonte gerado
automaticamente é o mesmo código-fonte que deveria ser criado manualmente por um programador
caso não existisse o gerador de código (DALGARNO, 2006, tradução nossa).
Os geradores de código podem ser divididos em dois grandes grupos denominados ativo e
passivo. De acordo Herrington (2003a, tradução nossa), um gerador de código passivo gera o
código-fonte e deixa as futuras manutenções no código-fonte gerado para os programadores. Já o
38
gerador de código ativo, gera o código-fonte e se responsabiliza pelas futuras manutenções do
código-fonte gerado.
2.2.1 Vantagens e Desvantagens
A utilização de geradores de código durante o ciclo de desenvolvimento de software
apresenta vantagens tanto para programadores quanto para projetistas de software, conforme pode
ser observado em Herrington (2003a, tradução nossa), Herrington (2003b, tradução nossa) e
Cleaveland (1988, tradução nossa):
• Qualidade e Consistência: Todo o código criado pelo gerador apresenta qualidade
uniforme, pois a fonte de geração é única. Logo, caso deseja-se incrementar a qualidade
do código gerado, deve-se melhorar o gerador de código ou os templates utilizados pelo
gerador. Portanto, qualquer correção de bug realizada no gerador de código ou nos
templates utilizados corrigirá todo o código gerado pela aplicação;
• Produtividade: A utilização de um gerador de código produz código de maneira muito
mais rápida que a codificação manual. No entanto, não é apenas neste ponto que a
produtividade é incrementada. O grande ganho ocorre quando pode-se alterar as
especificações utilizadas pelo gerador de código e facilmente gerar todo o código
novamente baseado em tais requisitos;
• Abstração: A geração de código permite abstrair os detalhes de implementação do
programador. Esta abstração pode ser, por exemplo, o modo como a aplicação acessa o
banco de dados;
• Redução dos erros de programação: A geração de código permite que o projetista se
concentre nos erros de especificação; e
• Facilidade de prototipação e testes: Os geradores de código facilitam a construção de
protótipos, bem como a especificação de testes alternativos.
Herrington (2003a, tradução nossa), afirma que apenas geradores de código ativos são
capazes de prover todas as vantagens que a técnica de geração de código oferece. Entretanto, para
alguns autores, algumas das vantagens em utilizar a geração de código no ciclo de desenvolvimento
de software também são as desvantagens de tais ferramentas, conforme citado por Sosnoski (2003,
tradução nossa) e Rutherford e Wolf (2003, tradução nossa):
39
• Abstração: No início do processo de desenvolvimento de software, a abstração dos
detalhes de implementação certamente fornece benefícios. No entanto, durante as fases
de testes e manutenção do sistema, a falta de conhecimento sobre o código gerado pode
ser um problema para o programador; e
• Consistência: Pelo fato de todas as interfaces e classes serem geradas de maneira
uniforme, toda a aplicação fica amarrada a estrutura utilizada pelo gerador de código.
Logo, caso ocorra alguma alteração na estrutura das classes e interfaces geradas, todo o
resto da aplicação que utiliza o código gerado também deverá ser alterado.
Conforme observado na literatura, em alguns casos, o que geralmente representa uma
vantagem na utilização de geração de código, pode ser definido como uma desvantagem em outros
casos, e vice-versa. Por exemplo, a abstração pode ser vantajosa durante a fase desenvolvimento,
pois abstrai do programador os detalhes de implementação. No entanto, tais detalhes de
implementação podem ser importantes para permitir a criação de testes mais complexos durante a
fase de validação do software.
De acordo com Herrington (2003b, tradução nossa), a geração de código é ideal para pro
blemas bem conhecidos, como por exemplo, camadas de acesso a bancos de dados, stored
procedures, ou qualquer outro problema cuja arquitetura seja bem definida. Quando a estabilidade
do código a ser gerado ou da arquitetura utilizada é duvidosa, não é aconselhável a utilização de
geradores de código. Para que se torne possível a utilização de geradores em tais situações faz-se
necessário que alguns protótipos sejam codificados manualmente antes da utilização do gerador de
código, tendo como objetivo, adquirir conhecimento sobre a arquitetura e código a serem gerados.
Franca e Staa (2002, tradução nossa) afirmam que os protótipos devem ser construídos de modo que
cada novo protótipo construído seja mais complexo que o anterior, possibilitando a validação e
verificação da arquitetura utilizada, pois o gerador de código replicará esta arquitetura no código-
fonte gerado. Cleaveland (1988, tradução nossa) observa que definir quando um gerador de código
pode ou não ser utilizado é um processo difícil, e muitas vezes, ocorre muito tarde no ciclo de
desenvolvimento.
2.2.2 Preocupações Sobre a Geração de Código
Durante a proposta de utilização, desenvolvimento ou implantação de um gerador de código
é possível que sejam levantadas algumas questões relacionadas à ferramenta. Estas questões podem
40
ser preocupações técnicas ou até mesmo não terem nenhum fundamento. As questões mais comuns,
listadas abaixo, que freqüentemente preocupam os membros da equipe de desenvolvimento são
citadas por Herrington (2003a, tradução nossa) e Herrington (2003b, tradução nossa):
• Ninguém no time de desenvolvimento aceitará um gerador de código: Certamente ao
utilizar uma ferramenta que pode fazer em minutos o que antes levava semanas ou meses
para ser feito pode ter um efeito drástico na equipe de desenvolvimento, especialmente
se ninguém da equipe tiver experiência com geradores de código. Pode ser até que
ocorra a resistência de alguns programadores em aceitar a ferramenta. Para que isso não
aconteça, é possível adotar algumas estratégias, como: (i) iniciar de maneira simples,
escolhendo apenas uma pequena parte do código para ser gerada; (ii) integrar com a
arquitetura já existente, deixando uma possível migração de arquitetura para um segundo
passo, após os testes e estabilização do código gerado; (iii) resolver inicialmente um
único problema, avançando na resolução de outros problemas com o passar do tempo;
• Desenvolvedores irão ignorar os comentários “não editar”: Um dos problemas mais
comuns com o código gerado é a quebra da regra de não editar o código, geralmente
lembrada através de comentários no próprio arquivo de código-fonte. É claro que não há
garantia de que o código-fonte gerado não será alterado manualmente por um
programador. Para que isso não aconteça, é necessário educar os programadores para
que respeitem a regra de não editar o código-fonte, bem como adicionar novas
funcionalidades ao gerador de código para que as necessidades dos programadores sejam
atendidas pela ferramenta; e
• Não haverá manutenção para o gerador de código: A falta de manutenção no gerador de
código realmente é uma preocupação muito bem fundamentada. Um gerador de código é
igual a qualquer outra aplicação e precisa sofrer manutenções com o passar do tempo,
para que novas funcionalidades sejam adicionadas. Isto garante que o código-fonte será
gerado pela ferramenta, ao invés de ser codificado manualmente pelos programadores.
2.2.3 Formas de Geração de Código Ativa
Há várias maneiras de categorizar geradores de código ativos. Pode-se caracterizá-los pela
sua complexidade, utilidade, ou pela saída gerada. De acordo com Herrington (2003b, tradução
nossa) e Dalgarno (2006, tradução nossa) os geradores de código ativos podem ser divididos em
41
cinco categorias, levando em consideração a entrada e saída de dados, conforme detalhado nas
subseções abaixo.
2.2.3.1 Code Munger
O gerador de código categorizado como code munger lê um arquivo de código-fonte como
entrada de dados e gera um novo arquivo de código-fonte como saída de dados (DALGARNO,
2006, tradução nossa). Durante este processo, observa Herrington (2003b, tradução nossa), o
munger seleciona algumas características do arquivo de entrada e utiliza estas características para
criar um ou mais arquivos de saída. Este tipo de gerador de código pode ser utilizado para ler
constantes ou protótipos de funções de um arquivo. A Figura 13 ilustra o fluxo de geração de
código deste tipo de gerador.
Figura 13. Fluxo de geração de código do code munger
Fonte: Adaptado de Herrington (2003b).
2.2.3.2 Inline-Code Expander
Conforme Dalgarno (2006, tradução nossa), este tipo de gerador de código lê um arquivo de
código-fonte como entrada de dados e gera como saída um novo arquivo de código-fonte. O arquivo
de entrada de dados possui seções especiais que são substituídas pelo novo código-fonte gerado
pelo inline-code expander (HERRINGTON, 2003b, tradução nossa).Um exemplo de utilização
deste tipo de gerador de código é utilizado pela linguagem Pro*C, onde o programador escreve
comandos SQL e o gerador produz código C que implementa os comandos e consultas SQL
especificadas no arquivo de entrada (DALGARNO, 2006, tradução nossa). A Figura 14 mostra
fluxo de geração de código do gerador inline-code expander.
42
Figura 14. Fluxo de geração de código do inline-code expander
Fonte: Adaptado de Herrington (2003b).
2.2.3.3 Mixed-Code Generator
O mixed-code generator, observa Herington (2003b, tradução nossa), lê um arquivo de
código-fonte como entrada de dados, modifica-o e coloca o código-fonte gerado de volta no mesmo
arquivo. A diferença entre este tipo de gerador de código e o inline-code expander, é que o
expander gera um novo arquivo como saída, enquanto que o mixex-code generator gera o código-
fonte no mesmo arquivo utilizado para entrada de dados. O mixed-code generator procura por
comentários especiais no arquivo de entrada, e substitui estes comentários pelo novo código-fonte
necessário. Dalgarno (2006, tradução) cita que os wizards geralmente são implementados como
sendo um mixed-code geneator. A Figura 15 mostra o fluxo de geração de código do mixed-code
generator.
43
Figura 15. Fluxo de geração de código do mixed-code generator
Fonte: Adaptado de Herrington (2003b).
2.2.3.4 Partial-Class Generator
Este tipo de gerador de código lê um modelo de especificação abstrata como entrada de
dados e então gera o arquivo de saída com base em tais definições (DALGARNO, 2006, tradução
nossa). Herrington (2003b, tradução nossa) cita que podem ser utilizados arquivos templates para
especificar a formatação do código-fonte a ser gerado. Após a geração do código-fonte realizada
pelo gerador de código, é necessário que o programador estenda o código gerado, ou seja, codifique
manualmente as outras classes que formarão o conjunto de classes utilizadas em produção, podendo
herdar as mesmas das classes geradas automaticamente (DALGARNO, 2006, tradução nossa). Um
gerador de código do tipo partial-class generator é um bom ponto de partida para criar um gerador
que seja capaz de criar uma camada inteira da aplicação. Conforme o gerador torna-se capaz de
gerenciar mais casos, pode-se realizar a transição para um gerador de código capaz de criar uma
camada inteira (HERRINGTON, 2003b, tradução nossa). A Figura 16 mostra o fluxo de entrada e
saída de dados em um partial-class generator. Já a Figura 17 mostra o processo de geração de
código de um partial-class generator para uma camada de acesso a dados.
44
Figura 16. Fluxo de entrada e saída de dados de um partial-class generator
Fonte: Adaptado de Herrington (2003b).
Figura 17. Processo de geração de código de um partial-class generator para uma camada de acesso a dados
Fonte: Adaptado de Herrington (2003b).
2.2.3.5 Tier or Layer Generation
Um gerador de código deste tipo, conforme cita Dalgarno (2006, tradução nossa), é capaz de
gerar o código para uma camada completa em um sistema n-camadas, não havendo a necessidade
de codificação adicional no código gerado. O gerador de código lê um arquivo de especificação
45
abstrata e gera o código baseado nesta especificação, havendo a possibilidade de utilizar arquivos
templates para formatação do arquivo de saída (HERRINGTON, 2003b, tradução nossa). O fluxo
de entrada e saída de dados de um tier or layer generator é semelhante ao apresentado para os
geradores de código do tipo partial-class generator na Figura 17.
A principal diferença entre um partial-class generator e um tier or layer generator, é que o
primeiro gera apenas as classes bases, sendo necessária a implementação de classes derivadas ou
novas classes para completar o conjunto de classes necessárias pela camada. Já geradores de código
do tipo tier or layer generator são capazes de gerar camadas inteiras para uma aplicação, não sendo
necessária nenhuma codificação manual (HERRINGTON, 2003b, tradução nossa). A Figura 18
mostra a utilização de um tier or layer generator para construir a camada de acesso a um banco de
dados em um sistema três camadas.
Figura 18. Processo de geração de código do tier or layer generator para uma camada de acesso a dados
Fonte: Adaptado de Herrington (2003b).
2.2.4 Geração de Código para a Camada de Acesso a Dados
De acordo com Herrington (2003b, tradução nossa), geração de código e classes de acesso a
dados formam uma parceria ideal, pois na maioria das vezes o código necessário para acessar uma
fonte de dados é bem estruturado e repetitivo. A utilização de um gerador de código para as classes
de acesso a dados, permite que os desenvolvedores trabalhem em problemas mais interessantes,
como por exemplo, a codificação das regras de negócio do sistema, ao invés de gastar tempo em
uma codificação extremamente repetitiva. Além disso, elimina a possibilidade de ocorrerem erros
46
comuns de codificação devido a utilização das ações “copiar/colar”, onde o desenvolvedor codifica
o código, copia tal codificação, e começa a replicar este código por toda a aplicação. Com isso, é
possível que o desenvolvedor esqueça de realizar alguma alteração, resultando em erros de
codificação.
Quando se comenta sobre acesso a fonte de dados, é comum pensar em acesso a um banco
de dados. No entanto, algumas aplicações não necessitam de um banco de dados para persistir
informações, podendo utilizar algum outro mecanismo de persistência. Nestes casos, e em qualquer
outro em que haja a necessidade de armazenar e pesquisar dados, os fundamentos de geração de
código também podem ser utilizados, independentemente do mecanismo de persistência utilizado
pela aplicação (HERRINGTON, 2003b, tradução nossa). No entanto, a utilização de bancos de
dados para persistência das informações é muito comum. O que diferencia os geradores de código
para acesso a um banco de dados é a maneira como os geradores pegam a entrada de dados. Os
possíveis tipos de geração baseados nesta premissa e citados por Herrington (2003a, tradução nossa)
são:
• Geração de código a partir de um banco de dados: Neste caso, o gerador acessa o banco
de dados especificado e pega as informações das tabelas do banco de dados. Com base
nestas informações, o gerador constrói as classes que mapeiam as tabelas e respectivos
campos do banco de dados especificado;
• Geração de código a partir de marcações: O gerador lê marcações XML em um arquivo
de código-fonte que descrevem as operações e consultas que deve ser realizadas ao
banco de dados. Em seqüencia, o gerador adiciona ao arquivo o código que implementa
as especificações encontradas no arquivo de entrada; e
• Geração de código a partir de uma definição abstrata: O gerador trabalha com um
arquivo que contém informações abstratas sobre a estrutura do banco de dados.
Geralmente, este arquivo está em formado XML e é gerado por uma ferramenta de
modelagem UML. Com base neste arquivo de especificação abstrata o gerador de código
gera as classes de acesso ao banco de dados.
2.2.5 NVelocity
NVelocity permite a geração de código-fonte, relatórios e outras saídas geradas a partir de
um template (APACHE, 2007a, tradução nossa). Trata-se de um projeto open-source publicado e
47
disponível para download no site da Source Forge (SOURCEFORGE, 2003). A idéia deste projeto
é portar o projeto Jakarta Velocity da plataforma Java para a plataforma .Net, mantendo a maior
similaridade possível entre os projetos (SOURCEFOURGE, 2003, tradução nossa). Vale lembrar
que o modelo de arquitetura utilizado no projeto Jakarta Velocity foi replicada no NVelocity,
permitindo que o mesmo conhecimento possa ser utilizado em ambas plataformas, Java e .Net.
Para escrever os arquivos de templates para o motor NVelocity, deve ser utilizada a
linguagem VTL (Velocity Template Language). Através desta linguagem é possível escrever os
arquivos templates e definir como será gerada a saída desejada. Para que seja possível a geração de
conteúdo dinâmico na saída desejada, objetos, constantes e/ou variáveis definidas na linguagem de
programação podem ser mapeados para as referências existentes no arquivo template (APACHE,
2007b, tradução nossa). Detalhes mais específicos sobre a linguagem VTL podem ser observados
em Silva (2007) e Apache (2007c).
Para que tal mapeamento possa ser realizado, deve-se utilizar um contexto. Através do
contexto torna-se possível definir os dados de entrada utilizados pelo template, permitindo a
posterior formatação dos dados. Ao efetuar o mapeamento deve ser informado tanto o valor a ser
utilizado como entrada quanto o nome da referência para este objeto no arquivo template (SILVA,
2007). A Figura 19 mostra um trecho de código que pode ser utilizado para mapear os valores de
entrada e as referências do template.
VelocityContext context = new VelocityContext(); context.Put("from", "somewhere"); context.Put("to", "someone"); context.Put("subject", "Welcome to NVelocity"); context.Put("customer", new Customer("John Doe") );
Figura 19. Código utilizado para mapear os valores de entrada e as referências do template
Fonte: Castleproject (2007)
No código-fonte mostrado na Figura 19, é criado um objeto context, passando a ele os
valores para quatro referências (from, to, subject, customer) definidas no arquivo template.
Qualquer lugar do arquivo template que utilize tais referências será substituído pelos valores
passados como entrada, ou seja, from será substituído por somewhere, to por someone, subject por
Welcome to NVelocity e customer pela instância da classe Customer. Vale lembrar que os atributos
e/ou propriedades públicas da classe Customer podem ser utilizados no arquivo template através da
48
referência customer. Os valores utilizados para entrada de dados podem ser tanto uma constante,
variável ou um objeto.
2.2.5.1 Utilizando o NVelocity
Para utilizar o NVelocity é necessário ter uma instância do motor de templates e um ou mais
arquivos templates. Além destes dois componentes, faz-se necessário também a instância de um
contexto que conterá as informações que serão utilizadas como entrada de dados para o template.
Ao processo em que é realizada a junção das informações do template com os dados de entrada
definidos no contexto dá-se o nome de merge (CASTLEPROJECT, 2007, tradução nossa).
Primeiramente deve ser criado o arquivo template, escrito de acordo com as especificações
da linguagem VTL, salvando tal arquivo em formato texto. A Figura 20 mostra um exemplo de
arquivo template escrito em VTL.
From: $from To: $to Subject: $subject Hello $customer.Name
Figura 20. Exemplo de arquivo template escrito em VTL
Fonte: Adaptado de Castleproject (2007)
Tendo o arquivo template criado, pode-se utilizar o NVelocity em qualquer linguagem da
plataforma .Net para realizar o processo de merge. A Figura 21 mostra um código-fonte
demonstrando a utilização do motor de templates NVelocity e a Figura 22 mostra o resultado
gerado pelo merge. O arquivo template utilizado para gerar tal resultado apresentado na Figura 22 é
o template apresentado na Figura 20.
49
class Program { static void Main(string[] args) { //Inicializa o motor de template NVelocity. VelocityEngine velocity = new VelocityEngin e(); ExtendedProperties props = new ExtendedProp erties(); velocity.Init(props); //Carrega o template. Template template = velocity.GetTemplate(@" TemplateTest.vm"); //Efetua mapeamento de valores de entrada c om referências do template. VelocityContext context = new VelocityConte xt(); context.Put("from", "Francis"); context.Put("to", "Fabiane"); context.Put("subject", "Welcome to NVelocit y"); context.Put("customer", new Customer("Fabia ne")); //Efetua o merge. StringWriter writer = new StringWriter(); template.Merge(context, writer); //Mostra resultado na tela e aguarda pressi nar uma tecla para sair. Console.WriteLine(writer.GetStringBuilder() .ToString()); Console.ReadKey(); } }
Figura 21. Utilização do motor de templates NVelocity
Fonte: Adaptado de Castleproject (2007)
Figura 22. Resultado gerado pelo merge
2.3 ACESSO A METADADOS DE UM BANCO DE DADOS
De acordo com Borrie (2004, tradução nossa), os objetos definidos dentro de um banco de
dados representam os metadados do banco de dados. Metadados representam dados sobre dados, e
devem ser armazenados em tabelas do próprio banco de dados, para facilitar a utilização de tais
informações (NIELSEN, 2007, tradução nossa). Conforme Codd (1990, tradução nossa), o conjunto
de tabelas utilizado para armazenar os metadados do banco de dados denomina-se catálogo. Através
do acesso a estes metadados, é possível recuperar todas as informações sobre os objetos criados no
banco de dados.
50
A API MyMeta (MYMETA, 2007a), é um assembly COM (Component Object Model),
compatível com o Microsoft .NET framework, que permite recuperar metadados de diferentes tipos
de bancos de dados, sendo composta por classes que descrevem genericamente as tabelas, visões e
procedimentos de um banco de dados (AVERY; HOLMES, 2006, tradução nossa). Pertence a um
projeto open source denominado MyGeneration (MYGENERATION, 2007), que utiliza a licença
de distribuição conhecida como BSD (Berkeley Software Distribution), permitindo que qualquer
parte do projeto seja utilizada e/ou modificada, seja para fins comerciais ou não, desde que as
condições impostas pela licença sejam atendidas. De acordo com MyMeta (2007b, tradução nossa),
com a utilização desta API é possível recuperar os metadados dos seguintes bancos de dados:
Microsoft Access 97 ou superior, IBM DB2, MySQL, Oracle 8i e 9i, Microsoft SQL Server 2000
ou superior, Pervasive 9.00 ou superior, PostgreSQL 7.3 ou superior, Firebird, Borland’s Interbase,
SQLite, VistaDB Database, Advantage Database Server, iSeries (AS400).
2.3.1 Estrutura
A estrutura da API MyMeta é composta por classes que descrevem genericamente objetos
de um banco de dados como tabelas, visões, procedimentos, domínios, índices e chaves
estrangeiras. A Figura 23 mostra o diagrama de classes UML contendo as principais classes da API
MyMeta.
51
Figura 23. Diagrama de classes da API MyMeta
A seguir, são descritas as principais classes apresentadas na Figura 23, de acordo com
MyMetaHelp (2007b, tradução nossa):
• Database: Representa um banco de dados de um DBMS (Database Management
System). Os principais atributos desta classe são Domains, Procedures, Tables e Views,
que são coleções de objetos que representam respectivamente os domínios,
procedimentos, tabelas e visões do banco de dados;
52
• Domain: Representa um domínio de um banco de dados. Nielsen (2007, tradução nossa)
define um domínio como um conjunto de valores para um atributo, como por exemplo,
valores inteiros, caracteres, valores nulos, entre outros, garantindo que apenas valores
válidos sejam definidos para um atributo. Os principais atributos desta classe são Name,
DataTypeName e NumericPrecision que representam o nome, tipo e precisão numérica
do domínio, respectivamente. Ainda podem ser citados os atributos HasDefault e
IsNullable que indicam, respectivamente, se o domínio possui um valor padrão e se
aceita valores nulos;
• Procedure: Representa um procedimento armazenado ou uma função de um banco de
dados. Um procedimento armazenado ou uma função é um programa escrito e
compilado no próprio banco de dados e armazenado como código executável nos
metadados do banco de dados, podendo ser invocado por uma aplicação externa
(BORRIE, 2004, tradução nossa). Os principais atributos desta classe são Name e
ProcedureText que representam, respectivamente, o nome e o código de definição do
procedimento ou função. Os atributos Parameters e ResultColumns representam uma
coleção de parâmetros e uma coleção de valores retornados pelo procedimento ou
função, respectivamente;
• Table: Representa uma tabela do banco de dados. De acordo com Borrie (2004, tradução
nossa) e Codd (1990, tradução nossa), uma tabela pode ser visualizada como uma
estrutura bidimensional, composta por colunas, os atributos, e linhas, os registros ou
tuplas, tendo como objetivo armazenar dados. Os principais atributos desta classe são
Name e Description, que representam o nome e a descrição da tabela, respectivamente.
Podem ser citados ainda os atributos Columns, ForeignKeys, Indexes e PrimaryKeys que
representam, respectivamente, coleções com todas as colunas, chaves estrangeiras,
índices e colunas que formam a chave primária da tabela;
• ForeingKey: Representa uma chave estrangeira de uma tabela de um banco de dados.
Uma chave estrangeira é uma coluna ou conjunto de colunas de uma tabela que
corresponde, exatamente na mesma ordem, a uma coluna ou conjunto de colunas
definidas como chave primária ou chave única em outra tabela (BORRIE, 2004,
tradução nossa). Os principais atributos desta classe são Name e ForeignColumns que
representam o nome e o conjunto de colunas que formam a chave estrangeira,
respectivamente;
53
• Index: Representa um índice em uma tabela de um banco de dados. Borrie (2004,
tradução nossa) afirma que um índice serve como um ponteiro lógico para a localização
física de um registro, e é atribuído a uma coluna ou conjunto de colunas de uma tabela
de um banco de dados. Os principais atributos desta classe são Name e Table que
representam, respectivamente, o nome do índice e a tabela à qual o índice está associado.
Pode ser citado ainda o atributo Columns que é uma coleção contendo todas as colunas
da tabela que compõem o índice; e
• View: Representa uma visão de um banco de dados. Uma visão pode ser descrita como
uma tabela virtual, pois todos os dados apresentados pela visão estão armazenados nas
tabelas físicas do banco de dados. A única informação relacionada a uma visão que é
armazenada nos metadados de um banco de dados é o comando que descreve a origem
dos dados apresentados pela visão (BORRIE, 2004, tradução nossa). Codd (1990,
tradução nossa) observa que as visões podem ser utilizadas como uma maneira de
restringir o acesso de um usuário ao banco de dados, permitindo que o usuário interaja
com o banco de dados apenas através das visões. Nesta classe, podem ser citados os
atributos Name e Columns que representam o nome da visão e uma coleção contendo
todas as colunas que compõem a visão. Os atributos SubTables e SubView são coleções
que contém todas as tabelas e visões, respectivamente, referenciadas diretamente pela
visão.
Todas as classes mostradas na Figura 23 representam classes base na implementação da API
MyMeta. Para cada um dos tipos de bancos de dados suportados pela API, são implementadas
classes especializadas/herdadas das classes base. Logo, uma tabela do banco de dados Oracle é
mapeada para uma classe denominada OracleTable, herdando todos os membros da classe base
Table e especializando alguns deles, caso necessário. Esta especialização é realizada para cada tipo
de banco de dados suportado, em todas as classes da API que representam um objeto de um banco
de dados.
2.3.2 Recuperando Metadados com a API MyMeta
Conforme dito anteriormente, a API MyMeta permite recuperar metadados de diferentes
tipos de bancos de dados. O primeiro passo necessário para recuperar os metadados de um banco de
dados é efetuar a conexão com o banco de dados desejado, que é estabelecida através de um driver
OLE DB (Object Linking and Embedding Database). Para que isso se torne possível, é necessário
54
instanciar um objeto da classe MyMeta e invocar o método Connect, passando por parâmetro o tipo
de banco de dados desejado e a string de conexão compatível com o banco de dados escolhido
(MYMETA, 2007b, tradução nossa). A Figura 24 e a Figura 25 mostram o código, escrito em C#,
necessário para efetuar a conexão com um banco de dados SQL Server e Oracle, respectivamente,
utilizando a API MyMeta.
MyMeta.dbRoot myMeta = new MyMeta.dbRoot(); string connectionstring = @"Provider=DRIVER_SQLServ erOLEDB; Data Source=SQLSERVER_I NSTANCE; Initial Catalog=DATABAS E; User ID=USERNAME; Password=PWD"; myMeta.Connect(MyMeta.dbDriver.SQL, connectionstrin g); OU MyMeta.dbRoot myMeta = new MyMeta.dbRoot(); string connectionstring = @"Provider=DRIVER_SQLServ erOLEDB; Data Source=SQLSERVER_I NSTANCE; Initial Catalog=DATABAS E; User ID=USERNAME; Password=PWD"; myMeta.Connect("SQL", connectionstring);
Figura 24. Conexão ao banco de dados SQL Server através da API MyMeta
MyMeta.dbRoot myMeta = new MyMeta.dbRoot(); string connectionstring = @"Provider=DRIVER_OracleO LEDB; DataSource=ORACLE_INSTA NCE; Persist Security Info=T rue; User ID=USERNAME; Password=PWD;"; myMeta.Connect(MyMeta.dbDriver.Oracle, connectionst ring); OU MyMeta.dbRoot myMeta = new MyMeta.dbRoot(); string connectionstring = @"Provider=DRIVER_OracleO LEDB; DataSource=ORACLE_INSTA NCE; Persist Security Info=T rue; User ID=USERNAME; Password=PWD;"; myMeta.Connect("ORACLE", connectionstring);
Figura 25. Conexão ao banco de dados Oracle através da API MyMeta
Conforme pode ser observado na Figura 24 e na Figura 25, o método Connect é
sobrecarregado para aceitar tanto um item de uma enumeração nomeada dbDriver, quanto uma
string para determinar a qual banco de dados deverá ser estabelecida a conexão.
Após obter a conexão com o banco de dados, o próximo passo é acessar as propriedades da
classe IDatabase para obter as informações referentes as tabelas, visões, procedimentos
armazenados, entre outros (MYMETA, 2007b, tradução nossa). A Figura 26, Figura 27 e Figura 28
55
mostram o código, escrito em C#, para recuperar informações de tabelas, visões e procedimentos
armazenados de um banco de dados, respectivamente.
MyMeta.IDatabase db = myMeta.DefaultDatabase; //Percorre todas as tabelas do banco de dados. foreach (MyMeta.ITable table in db.Tables) { //Escreve o nome da tabela. Console.WriteLine("TableName: {0}", table.Name) ; //Percorre todas as colunas da tabela. foreach (MyMeta.IColumn col in table.Columns) { //Escreve dados da coluna como nome, tipo e se aceita valores nulos. Console.WriteLine("\tColumnName:{0}, DataTy pe:{1}, Nullable:{2}", col.Name, col.DataTypeNam e, col.IsNullable); } //Percorre todos os índices da tabela. foreach (MyMeta.IIndex idx in table.Indexes) { //Escreve dados do índice como o nome e se representa um índice único. Console.WriteLine("\tIndexName:{0}, Unique: {1}", idx.Name, idx.Unique); } //Percorre todas as chaves estrangeiras da tabe la. foreach (MyMeta.ForeignKey fk in table.ForeignK eys) { //Escreve o nome da chave estrangeira. Console.WriteLine("\tFKName{0}", fk.Name); } }
Figura 26. Código escrito em C# para retornar informações de tabelas
MyMeta.dbRoot myMeta = new MyMeta.dbRoot(); //Percorre todas as visões do banco de dados. foreach (MyMeta.IView view in db.Views) { //Escreve o nome da visão. Console.WriteLine("ViewName:{0}", view.Name); //Percorre todas as tabelas utilizadas pela vis ão. foreach (MyMeta.ITable table in view.SubTables) { //Escreve o nome da tabela utilizada pela v isão. Console.WriteLine("TableName: {0}", table.N ame); } //Percorre todas as colunas da visão. foreach (MyMeta.IColumn col in view.Columns) { //Escreve o nome e o tipo da coluna. Console.WriteLine("\tColumnName:{0}, DataTy pe:{1}", col.Name, col.DataTypeNam e); } }
Figura 27. Código escrito em C# para retornar informações de visões
56
MyMeta.dbRoot myMeta = new MyMeta.dbRoot();
//Percorre todos os procedimentos e funções do banc o de dados.
foreach (MyMeta.IProcedure proc in db.Procedures) {
//Escreve o nome do procedimento ou função.
Console.WriteLine("\tProcedureName: {0}", proc. Name);
//Percorre todos os parâmetros do procedimento ou função.
foreach (MyMeta.IParameter par in proc.Paramete rs) {
//Escreve o nome e o tipo do parâmetro.
Console.WriteLine("\t\tParamName:{0}, Param Type:{1}",
par.Name, par.LocalT ypeName);
}
//Percorre todos os valores retornados pelo pro cedimento ou função.
foreach (MyMeta.IResultColumn res in proc.Resul tColumns) {
//Escreve o nome e o tipo do valor retornad o.
Console.WriteLine("\t\tResultName:{0}, Resu ltType:{1}",
res.Name, res.DataTy peName);
}
//Escreve a definição do procedimento ou função .
Console.WriteLine("\t\tProcedureBody:{0}", proc .ProcedureText);
}
Figura 28. Código escrito em C# para retornar informações de procedimentos e funções
O código necessário para obter a conexão com o banco de dados varia de acordo com o
banco de dados escolhido (Figura 24 e Figura 25). A partir do momento que a conexão foi
estabelecida, utiliza-se exatamente o mesmo código para obter informações sobre as tabelas, visões,
procedimentos armazenados, entre outros, independentemente do banco de dados selecionado
(Figura 26, Figura 27 e Figura 28). Isto demonstra que mesmo possuindo internamente uma classe
que representa um objeto de um banco de dados, como por exemplo a classe Table, para cada tipo
de banco de dados suportado pela API MyMeta, tais especializações são transparentes para o
programador, garantindo abstração total sobre a estrutura da API.
2.4 FERRAMENTAS SIMILARES
Geradores de código permitem customizar e reutilizar uma arquitetura de software
facilmente, pois automatizam todo ou parte do processo de geração de código, aumentando a
produtividade durante o desenvolvimento e manutenção do software (CLEAVELAND, 1988,
tradução nossa). Atualmente, já existem soluções cujo objetivo é automatizar o processo de criação
de software através da geração de código, sejam elas ferramentas comerciais, onde é necessário
pagar por sua utilização, projetos open-source, ou até mesmo projetos acadêmicos.
57
2.4.1 Ferramentas Comerciais
Na categoria de ferramentas comerciais, podem ser citadas o Code Smith (CODESMITH,
2007a), ferramenta criada pela empresa Code Smith Tools, o Microsoft Visual Studio
(MICROSOFT, 2008a), ferramenta de desenvolvimento desenvolvida pela Microsoft Corporation, e
o IdeaBlade DevForce (IDEABLADE, 2007), desenvolvida pela empresa IdeaBlade.
O Code Smith é um gerador de código que utiliza arquivos templates com sintaxe similar ao
ASP.NET para formatar o arquivo de saída, permitindo gerar código para qualquer linguagem de
programação baseada em texto (CODESMITH, 2007a). O Code Smith possui uma funcionalidade
denominada SchemaExplorer, que mapeia informações de um banco de dados para um arquivo
XSD (XML Schema Definition). Com isso, é possível recuperar metadados de um banco de dados
SQL Server ou qualquer outro com conectividade ADO (ActiveX Data Objects), importar tais
informações para o SchemaExplorer e utilizá-las no processo de geração de código (CODESMITH,
2007b). A Figura 29 mostra o código template necessário para listar todas as tabelas de um banco
de dados selecionado pelo usuário através da IDE (Integrated Development Environment) Code
Smith Studio, interface de usuário avançada do Code Smith.
<%@ CodeTemplate Language="C#" TargetLanguage="Text " Description="List all database tables" %> <%@ Property Name="SourceDatabase" Type="SchemaExpl orer.DatabaseSchema" Category="Context" Description="Database containing the tables." %> <%@ Assembly Name="SchemaExplorer" %> <%@ Import Namespace="SchemaExplorer" %> Tables in database "<%= SourceDatabase %>": <% for (int i = 0; i < SourceDatabase.Tables.Count; i++) { %> <%= SourceDatabase.Tables[i].Name %> <% } %>
Figura 29. Código template utilizado pelo Code Smith para listar tabelas de um banco de dados
Fonte: Codesmith (2007b).
Esta ferramenta é comercializada em duas versões, a Standard e a Professional, pelo preço
de 99 e 399 dólares por usuário, respectivamente (CODESMITH, 2007a). O preço da licença do
Code Smith, pode limitar a sua utilização em alguns casos, pois para utilizá-lo é necessário adquirir
uma licença para cada desenvolvedor. Além disso, apenas a versão Professional fornece algumas
funcionalidades avançadas, que são listadas abaixo:
• IDE CodeSmith Studio: Interface de usuário avançada, que possui funcionalidades como
statement completion e debug dos templates;
58
• Integração com o Visual Studio 2005: Permite integrar as funcionalidades do Code
Smith dentro do Microsoft Visual Studio 2005;
• Suporte a merging: Permite efetuar o merging ou junção de arquivos de código-fonte.
Com isso, o código gerado de forma automática pode ser integrado ao código escrito
manualmente, caso assim desejado;
• Suporte ao MSBuild: Permite que a tarefa de geração de código realizada pelo Code
Smith seja invocada pelo MSBuild (Microsoft Build Engine), plataforma de compilação
do Microsoft Visual Studio; e
• Suporte ao ActiveSnippet: O ActiveSnippet é idêntico ao Code Snippet do Microsoft
Visual Studio. Através desta funcionalidade, é possível definir um apelido para um bloco
de código-fonte. Logo, durante a codificação, ao invés de reescrever todo o bloco de
código-fonte, pode-se apenas digitar o apelido definido e solicitar a expansão de código,
para que todo o código-fonte associado ao apelido seja escrito automaticamente no
editor de código-fonte.
O Microsoft Visual Studio é uma ferramenta RAD (Rapid Application Development) que
possui a capacidade de gerar código automaticamente. Esta IDE de desenvolvimento é capaz de
gerar comandos SQL, camadas de acesso a bancos de dados, interfaces com o usuário, integração
entre as camadas de modelo e visão, entre outras funcionalidades de geração de código
(MICROSOFT, 2008a). No entanto, para que todas estas funcionalidades sejam utilizadas é
necessária a aquisição de uma das versões pagas do produto que são o Microsoft Visual Studio 2005
Professional Edition, Microsoft Visual Studio Team System, Microsoft Visual Studio 2008 ou
Microsoft Visual Studio Team Suite, pois a versão gratuita possui limitações em relação à estas
características. Além do custo de aquisição do Microsoft Visual Studio, as principais desvantagens
desta ferramenta é que o código gerado é baseado exclusivamente no Microsoft .Net Framework e
não é possível utilizar templates para definir a formatação do código-fonte.
Outra ferramenta com a capacidade de gerar código, é o IdeaBlade DevForce, um plugin
para o Microsoft Visual Studio. O código-fonte gerado já separa os objetos de negócio dos objetos
de acesso a dados, conforme especificação do padrão DAO. No entanto, as principais desvantagens
do IdeaBlade. No é que o código-fonte gerado fica limitado às linguagens de programação C# ou
VB.NET, e conseqüentemente à plataforma .NET, e não permite utilizar templates para definir a
formatação do código-fonte. O código-fonte é gerado com base nos metadados de um banco de
59
dados existente, sendo suportado qualquer banco de dados através de conectividade ODBC (Open
Database Connectivity), OLE DB ou .NET providers nativos. A ferramenta é comercializada nas
versões Express, Professional, e Enterprise. A versão Express é gratuita e pode ser utilizada em
aplicações comerciais. No entanto, o Microsoft Visual Studio Express Edition, versão gratuita do
Microsoft Visual Studio, não permite integrar plugins à sua IDE. Portanto, é necessária a aquisição
de uma das versões pagas do Microsoft Visual Studio para que a versão Express do IdeaBlade
DevForce possa ser utilizada. A versão Professional é comercializada pelo valor de 2500 dólares
por desenvolvedor e para a aquisição da versão Enterprise é necessário entrar em contato direto
com o vendedor, pois o valor não encontra-se disponível no site da empresa (IDEABLADE, 2007).
2.4.2 Ferramentas Open-Source
Na categoria de ferramentas open-source podem ser citadas o TaHoGen (LAUREANO,
2005), projeto publicado na comunidade de código-fonte aberto CodeProject, e o Smart Code
Generator (KHAN et al, 2007), publicado na Code Plex, comunidade de código-fonte aberto para
projetos da plataforma .NET e incentivada pela Microsoft Corporation.
O TaHoGen é um gerador de código baseado em templates, que utiliza sintaxe semelhante
ao ASP.NET para definição dos arquivos templates. Esta ferramenta não possui uma IDE própria,
mas possui um plugin para o Microsoft Visual Studio (LAUREANO, 2005). No entanto, é
necessário ter uma das versões pagas do Microsoft Visual Studio, pois a versão Express não permite
a integração de plugins à sua IDE. A Figura 30 mostra um arquivo template utilizado pelo
TaHoGen para gerar os métodos getters e setters de uma classe.
<%@ CodeTemplate ClassName="PropertyGenerator" Name space=”MyTemplateNamespace” Language="C#" TargetLanguage="C#"%> <%@ Property Name="Name" Type="System.String" Categ ory="Options" %> <%@ Property Name="Type" Type="System.String" Categ ory="Options" %> <%@ Property Name="ReadOnly" Type="System.Boolean" Default="true" Category="Options" %> public <%=Type%> <%=Name%> { get { return _<%=Name.Substring(0, 1).ToLower() + Name.Substring(1)%>; }<%if (!ReadOnly) {%> set { _<%=Name.Substring(0, 1).ToLower() + Name.Substring(1)%> = value; }<%}%> }
Figura 30. Código template utilizado pelo TaHoGen criar os getters e setters de uma classe
Fonte: Laureano (2005).
60
O template mostrado na Figura 30 irá gerar uma classe denominada PropertyGenerator,
escrita em C#, dentro de um namespace denominado MyTemplateNamespace, com três
propriedades denominadas Name, Type e ReadOnly. A Figura 31 mostra o código-fonte da classe
gerada a partir do template mostrado na Figura 30.
namespace MyTemplateNamespace { public class PropertyGenerator : TaHoGen.Genera tors.TextGenerator { private string _name; private string _type; private bool _readOnly; public PropertyGenerator(){} public PropertyGenerator(TaHoGen.PropertyBa g propertyBag) : this() { this.LoadProperties(propertyBag); } [Category("Options")] public string Name { get { return this._name; } set { this._name = value; } } [Category("Options")] public string Type { get { return this._type; } set { this._type = value; } } [Category("Options")] public bool ReadOnly { get { return this._readOnly; } set { this._readOnly = value; } } protected override void GenerateImpl(System .IO.TextWriter writer) { writer.Write("\r\npublic "); writer.Write(Type); writer.Write(" "); writer.Write(Name); writer.Write("\r\n\t\t{\r\n\t\t\tget { return _"); writer.Write(Name.Substring(0, 1).ToLow er() + Name.Substring(1)); writer.Write("; }"); if (!ReadOnly) { writer.Write("\r\n\t\t\tset { _"); writer.Write(Name.Substring(0, 1).ToLow er() + Name.Substring(1)); writer.Write(" = value; }"); } writer.Write("\r\n\t\t}\r\n\t\t"); } } }
Figura 31. Código-fonte gerado a partir do template exibido na Figura 26
Fonte: Adaptado de Laureano (2005).
A classe PropertyGenerator fornece a possibilidade de definir valores para os seus atributos,
e com base nestas definições gerar o código-fonte. Neste caso, o código-fonte gerado será uma
propriedade de uma classe. Em C#, uma propriedade é a forma padrão de encapsular um atributo de
61
classe. Em Java, seu equivalente seriam os métodos get e set. A utilização da classe
PropertyGenerator para geração de código é exibida na Figura 32.
//Seta as propriedades para o template PropertyTable properties = new PropertyTable(); properties["Type"] = "string"; properties["Name"] = "MyProperty"; properties["ReadOnly"] = False;
Figura 32. Utilização da classe PropertyGenerator
Fonte: Adaptado de Laureano (2005).
A Figura 32 mostra que é necessário definir o valor para cada uma das propriedades da
classe para que o código seja gerado. A entrada de dados pode ser realizada através da integração do
TaHoGen dentro da aplicação desenvolvida ou através do pluggin para o Microsoft Visual Studio
(LAUREANO, 2005). Acredita-se que esta seja uma desvantagem em utilizar o TaHoGen, pois não
existe a capacidade de buscar automaticamente as informações necessárias para geração de código-
fonte de uma base de dados, algum modelo UML ou qualquer outro formato de arquivo existente.
O Smart Code Generator é um ASP.NET 2.0 web site ou aplicação web ASP.NET 1.1 que
permite a geração de código para qualquer linguagem de programação baseada em texto através da
utilização de templates. Todo o ciclo de desenvolvimento de templates é realizado utilizando o
Visual Studio, através da criação de ASP.NET User Controls (KHAN et al, 2007).
Uma classe denominada TheProperties é utilizada para customizar o código-fonte a ser
gerado, como por exemplo o nome da classe e o nome dos atributos que devem ser criados. O Smart
Code Generator cria então um site contendo campos que permitem ao usuário definir valores para
cada uma das propriedades definidas na classe TheProperties (SMARTCODEGENERATOR,
2006). A Figura 33 e a Figura 34 mostram um exemplo de código-fonte da classe TheProperties e a
interface de usuário criada pelo Smart Code Generator para definir valores para as propriedades da
classe TheProperties, respectivamente.
62
public class TheProperties { public TheProperties( ) { //Please Assign Default Values here this.ClassName = "TestClass"; } private string className; public string ClassName { get { return className; } set { className = value; } } }
Figura 33. Exemplo de código-fonte da classe TheProperties
Fonte: Smartcodegenerator (2006).
Figura 34. Interface gerada para definir os valores das propriedades da classe TheProperties
Fonte: Smartcodegenerator (2006).
Portanto, com base nas propriedades definidas para a classe TheProperties (Figura 33), o
Smart Code Generator gera um site que permite ao usuário definir os valores para as propriedades
da classe TheProperties (Figura 34). Para definir a formatação do código-fonte gerado, pode ser
utilizado um arquivo template. A Figura 35 e a Figura 36 mostram um exemplo de arquivo template
63
utilizado pelo Smart Code Generator e o código-fonte gerado a partir do template utilizado,
respectivamente.
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Example3Template.ascx.cs" Inherits="Templ ates_Example3Template" %> public class <%=this.TheClassName%> { public <%=TheProperties.ClassName%>() { //Add constructor here } private string <%=TheProperties.PropertyName.ToLo wer()%>; public string <%=TheProperties.PropertyName %> { get { return <%=TheProperties.PropertyName.ToLo wer()%>; } set { <%=TheProperties.PropertyName.ToLower()%> = value; } } }
Figura 35. Exemplo de arquivo template utilizado pelo Smart Code Generator
Fonte: Adaptado de Smartcodegenerator (2006).
public class TestClass { public TestClass() { //Add constructor here } private string testproperty public string TestProperty { get { return testproperty; } set { testproperty = value; } } }
Figura 36. Exemplo de código-fonte gerado a partir da utilização de template
Fonte: Adaptado de Smartcodegenerator (2006).
O Smart Code Generator permite também gerar código baseado nos metadados das tabelas
dos bancos de dados SQL Server, Oracle e MySQL. A Figura 37 e a Figura 38 mostram a interface
de usuário utilizada pelo Smart Code Generator para buscar os metadados de um banco de dados e o
código-fonte gerado, respectivamente.
64
Figura 37. Interface de usuário do Smart Code Generator para recuperação de metadados
Fonte: Adaptado de Smartcodegenerator (2006).
65
Figura 38. Código-fonte gerado a partir dos metadados de tabelas de um banco de dados
Fonte: Adaptado de Smartcodegenerator (2006).
Para recuperar os metadados, é necessário informar a string de conexão do banco de dados
desejado. Com base nesta string de conexão, o Smart Code Generator busca todas as tabelas
existentes e lista as mesmas para que o usuário possa selecionar as tabelas desejadas (Figura 37).
Após a seleção das tabelas desejadas, o usuário pode solicitar a geração do código-fonte (Figura
38).
66
Mesmo possuindo a capacidade de geração de código a partir de metadados, o Smart Code
Generator não permite ao usuário selecionar quais os atributos, de cada uma das tabelas
selecionadas, que devem ser gerados e nem customizar o nome das classes ou atributos gerados a
partir dos metadados. Com isso, o código gerado torna-se um mapeamento objeto/relacional puro,
sem a opção de customizações, o que em alguns casos pode ser necessário. Além disso, o Smart
Code Generator não fornece a opção de configurar os métodos de classe e os respectivos comandos
SQL utilizados por tais métodos para acesso e manipulação dos dados. Outra desvantagem em
utilizar o Smart Code Generator é a necessidade de utilização do Visual Studio, pois os ASP.NET
User Controls utilizados pela ferramenta devem ser criados pelo Visual Studio.
2.4.3 Ferramentas Acadêmicas
No meio acadêmico, também existem ferramentas desenvolvidas com o objetivo de
automatizar o processo geração de código. Alguns exemplos que podem ser citados são o Cordel
(GREVE et al, 2004), ferramenta desenvolvida na Universidade Federal da Bahia, e o XSpeed
(ROCHA et al, 2005), desenvolvida na Universidade Federal do Ceará
O Cordel utiliza como entrada de dados um modelo UML em formato XMI (XML Metadata
Interchange) e seus arquivos templates utilizam a sintaxe do motor de template conhecido como
Velocity. A partir do arquivo XMI e mais algumas configurações realizadas pelo usuário é
produzida a versão inicial da aplicação. A característica mais interessante do Cordel é que o código
gerado segue o modelo MDA (Model Driven Architecture). A Figura 39 mostra a arquitetura
interna do Cordel.
67
Figura 39. Arquitetura interna do Cordel
Fonte: Greve et al (2005).
Outra ferramenta cuja entrada de dados baseia-se em arquivos XMI é o XSpeed. De acordo
com Rocha et al (2005), o principal objetivo desta ferramenta é aumentar a produtividade no
desenvolvimento de aplicações para ambiente distribuído. O XSpeed, recebe um arquivo XMI
contendo o modelo UML da aplicação e então realiza a geração do código-fonte. Utiliza templates
escritos na sintaxe do motor de templates Velocity, mas o código gerado fica limitado à plataforma
Java. A Figura 40 mostra a arquitetura do XSpeed.
68
Figura 40. Arquitetura do XSpeed
Fonte: Rocha et al (2005).
2.4.4 Análise Comparativa
Além das ferramentas citadas anteriormente, podem ser citadas ainda o C# Data Tier
Generator (ANTTILA, 2001), LLBLGen Pro (SOLUTIONSDESIGN, 2007), FireStorm/DAO
(CODEFUTURES, 2007), RADPHP (SEARA, 2005), Gerador de código HTML baseado em
dicionário de dados utilizando banco de dados (COELHO, 2006), Ferramenta CASE para a geração
de páginas ASP (CASTILHOS, 2004) e Geração automática de cadastros e consultas para
linguagem ASP baseado em banco de dados (SILVEIRA, 2003). A Tabela 1 mostra o comparativo
entre todas as ferramentas citadas anteriormente e ferramenta proposta neste trabalho.
69
Tabela 1. Comparação entre as ferramentas similares
Fornecedor / Criador
Ferramenta Entrada de Dados Banco de Dados Linguagem de Saída
Características
Code Smith Code Smith Studio
Banco de dados, XSD Schema
SQL Server ou qualquer outro que forneça conectividade ADO
Qualquer linguagem baseada em texto
Ferramenta comercial. Utiliza templates com sintaxe semelhante ao ASP.NET para formatar o arquivo de saída
Microsoft Microsoft Visual Studio
Base de dados, Web-service, Objetos
Todos – através de driver ODBC, OleDb ou .NET providers nativos
Todas baseadas no .NET Framework
Ferramenta comercial. É capaz de gerar comandos SQL, camadas de acesso a banco de dados, interfaces com o usuário, integração entre as camadas de modelo e visão através da utilização de padrões conhecidos como o Observer, entre outras funcionalidades de geração de código
Idea Blade Idea Blade DevForce
Banco de dados
Todos – através de driver ODBC, OleDb ou .NET providers nativos
C# e VB.NET Ferramenta comercial. Pluggin para o Microsoft Visual Studio
Shaked Khan et al Smart Code Generator
Banco de dados SQL Server, Oracle e MySQL
Qualquer linguagem baseada em texto
Projeto open source. Utiliza templates com sintaxe semelhante ao ASP.NET para formatar o arquivo de saída, mas não permite ao usuário selecionar quais os atributos de uma tabela devem ser gerados, nem customizar as classes geradas a partir do mapeamento objeto/relacional
70
Philip Laureano TaHoGen
Pluggin do Visual Studio ou integração com aplicação
- Qualquer linguagem baseada em texto
Projeto open source. Utiliza templates com sintaxe semelhante ao ASP.NET para formatar o arquivo de saída
Adrian Anttila C# Data Tier Generator
Banco de Dados SQL Server 2000 Scripts SQL e C#
Projeto open source. Gera stored procedures com comandos SQL comuns e uma classe C# para utilizar tais stored procedures
Solution Design LLBLGen Pro Banco de Dados
Access, SQL Server,Oracle, PostgreSQL, Firebird, Interbase, IBM DB2, MySql, Sybase Adaptive Server Enterprise, Sybase SQL iAnywhere
C# e VB.NET
Ferramenta comercial. Permite salvar o mapeamento objeto/relacional realizado pelo usuário em formato XML
CodeFutures FireStorm / DAO
Script SQL e Banco de Dados
Todos - Através de driver JDBC (Java Database Connectivity)
Java Ferramenta comercial. Utiliza drivers JDBC para acessar as bases de dados
Fabíola Greve et al.
Cordel Modelo UML em formato XMI
- Qualquer linguagem baseada em texto
Trabalho acadêmico. Utiliza o motor de template Velocity para formatar o arquivo de saída. Gera código conforme o modelo MDA
Everton F. R. Seára
RADPHP Script SQL - PHP (Hypertext Preprocessor)
Trabalho acadêmico. Gera scripts em formato PHP baseado no framework PHPLib-UNIVALI
71
Lincoln S. Rocha et al.
XSpeed Modelo UML em formato XMI
- Java
Trabalho acadêmico. Utiliza o motor de template Velocity para a geração do código-fonte, mas o código gerado fica restrito à plataforma Java. Tem como objetivo gerar código para ambiente distribuído
Luis F. Coelho COELHO Dicionário de dados salvo em banco de dados Oracle
-
Scripts SQL e HTML (HyperText Markup Language)
Trabalho acadêmico. Utiliza o motor de template Velocity para a geração do código-fonte HTML e scripts SQL. O dicionário de dados deve ser cadastrado pelo usuário do sistema
Cristiano de Castilhos
CASTILHOS
Dicionário de dados salvo em banco de dados SQL Server ou Access
- ASP (Active Server Pages)
Trabalho acadêmico. O dicionário de dados deve ser cadastrado pelo usuário do sistema
Claudionor Silveira
SILVEIRA Banco de dados Access ASP Trabalho acadêmico. Gera sistema de cadastros e consultas completos baseados na plataforma Web
Francis Benito Odisi
Keops Banco de dados PostgreSQL, Oracle e SQL Server
Qualquer linguagem baseada em texto
Trabalho acadêmico. Utiliza o motor de templates NVelocity para permitir a geração de código-fonte para qualquer linguagem de programação baseada em texto. Permite salvar as configurações realizadas pelo usuário em formato XML.
72
Todas as ferramentas citadas anteriormente, com exceção do Code Smith, Smart Code
Generator e da ferramenta proposta, apresentam limitações relacionadas à possibilidade de
utilização de templates ou de acesso a um banco de dados existente para extração dos metadados,
ou seja, possuem apenas uma das funcionalidades citadas. Conforme já citado anteriormente, o
Code Smith, que possui as duas funcionalidades, é uma ferramenta comercial, cujo custo da licença
é cobrado por desenvolvedor (CODESMITH, 2007a). Já o Smart Code Generator, projeto open
source, não permite que o usuário selecione quais os atributos da classe devem ser gerados,
customize o nome das classes e atributos, nem permite a configuração de métodos de classe e
comandos SQL utilizados por tais métodos para acesso aos dados. Outra desvantagem desta
ferramenta, é a necessidade de utilizar o Visual Studio, pois utiliza ASP.NET User Controls que são
criados pelo Visual Studio (KHAN et al, 2007).
Portanto, uma ferramenta que consiga gerar código com base nos metadados de um banco de
dados, além de permitir a utilização de arquivos templates para formatação do código-fonte gerado,
apresenta um diferencial em relação à flexibilidade quando comparado com as ferramentas
pesquisadas. As principais vantagens da ferramenta proposta em relação às ferramentas citadas
anteriormente são: (i) a possibilidade de customização durante o processo de mapeamento
objeto/relacional através uma interface própria, onde é possível selecionar as tabelas e os atributos a
serem gerados; (ii) a possibilidade de realizar o mapeamento entre os tipos de dados dos bancos de
dados suportados e os tipos da linguagem de programação utilizada para geração do código-fonte;
(iii) a possibilidade de gerar código para qualquer linguagem de programação baseada em texto
através da utilização de templates; (iv) a possibilidade de ser utilizada tanto para o desenvolvimento
de uma aplicação completamente nova, onde todos os artefatos de software são desenvolvidos a
partir do zero, inclusive o banco de dados, quanto para a proposta de implementação de uma nova
arquitetura para um sistema legado já existente, onde o banco de dados existe há vários anos, e a
troca e remodelagem do mesmo é impraticável. Certamente a ferramenta proposta também
apresenta algumas desvantagens como: (i) limitação aos bancos de dados Oracle, PostgreSQL e
SQL Server; e (ii) falta de suporte para modelos UML em formato XMI.
De acordo com Microsoft (2008b, tradução nossa), pequenas aplicações geralmente utilizam
apenas uma linguagem de programação. Já no desenvolvimento de grandes aplicações, é comum a
utilização de diferentes linguagens de programação, onde os desenvolvedores envolvidos criam
componentes e serviços, e os disponibilizam através de sites remotos. Nestes casos, a melhor
escolha é utilizar várias linguagens, onde a linguagem de programação de cada equipe dependa das
73
habilidades e experiência dos desenvolvedores. Volanakis (2005, tradução nossa) cita que uma
ferramenta que permita escolher a linguagem de programação, fornece ao desenvolvedor a
possibilidade de escolher a linguagem de programação que ofereça a melhor solução para o
problema. Como exemplos de ferramentas que fornecem ao usuário esta característica podem ser
citados o Visual Studio (MICROSOFT, 2008b) e o Eclipse (ECLIPSE, 2008).
A utilização de uma ferramenta de geração de código possibilita a equipe de
desenvolvimento definir um padrão para o código-fonte gerado (CLEAVELAND, 1988, tradução
nossa). Caso opte-se por utilizar uma ferramenta para cada linguagem de programação, todas as
configurações realizadas em uma ferramenta devem ser replicadas nas demais. Com isso, todo o
trabalho já realizado uma vez precisa ser refeito em cada uma das ferramentas de geração utilizadas,
indo contra uma das principais vantagens fornecidas pela geração de código, observada em
Herrington (2003b, tradução nossa) e Cleaveland (1988, tradução nossa), que é o ganho de
produtividade. Além disso, diferentes ferramentas geralmente fornecem diferentes funcionalidades,
aumentando a possibilidade do código-fonte gerado não seguir a mesma arquitetura. Com isso, uma
das principais vantagens da geração de código, citada em Herrington (2003b, tradução nossa) e
Cleaveland (1988, tradução nossa), que é a qualidade e consistência do código gerado,
simplesmente deixa de existir.
A geração de código para as classes de acesso aos dados é aconselhada, pois na maioria das
vezes, o código é bem estruturado e repetitivo, ao contrário do código das regras de negócio e
interfaces de usuário. Gerando código para as classes de acesso aos dados, permite ao
desenvolvedor focar em aspectos normalmente mais indefinidos como a codificação das regras de
negócios ou interfaces de usuário. Além disso, o código de acesso aos dados é considerado a base
de um sistema. Gerando as classes que acessam tais dados, garante que as mesmas terão uma
arquitetura e interface consistente. Logo, torna-se mais fácil desenvolver as demais camadas do
sistema (HERRINGTON, 2003b, tradução nossa). A geração de código para as classes de acesso a
dados permite ainda definir um padrão de arquitetura para todo o código gerado, além de facilitar a
correção de bugs comuns como, erros na ordenação dos parâmetros de um comando SQL,
tratamento inadequado das exceções, entre outros (HERRINGTON, 2003a, tradução nossa).
74
3 PROJETO
Nesta seção são descritas as características referentes à modelagem da ferramenta de geração
de código proposta. A técnica de geração de código utiliza um programa para gerar outro programa
ou parte dele. Para realizar esta tarefa, o gerador de código recebe como entrada uma especificação
de alto nível, convertendo-a em programas ou parte de programas. Conforme abordado na seção
2.2, as ferramentas de geração de código podem ser classificadas em dois grandes grupos
denominados ativo e passivo. Devido ao fato da ferramenta proposta permitir que futuras
manutenções no código-fonte gerado sejam realizadas através da própria ferramenta, esta pode ser
classificada como um gerador de código ativo.
Para tal, nesta seção são apresentadas as regras de negócios, bem como os requisitos
funcionais e não funcionais do sistema. Além disso, são mostrados os diagramas de casos de uso e
diagramas de classes utilizando a notação UML. Também é descrito o XSD que define os
documentos XML utilizados para armazenar as configurações realizadas pelo usuário, com relação
às classes a serem geradas. Por último, é mostrado um exemplo de código-fonte a ser gerado pelo
sistema.
Desta forma, as seções subseqüentes apresentam as regras de negócio, definição dos
requisitos funcionais e não funcionais do sistema, a especificação dos diagramas de casos de uso,
diagramas de classe, XML Schema, e um exemplo de código-fonte a ser gerado pela aplicação.
3.1 DEFINIÇÃO DOS REQUISITOS E REGRAS DE NEGÓCIOS
Em qualquer sistema computacional, é importante definir as regras de negócio e antever
quais as funções que devem ser realizadas pelo sistema antes de iniciar sua implementação.
Portanto, nesta seção são descritos os requisitos funcionais e não funcionais, bem como as regras de
negócio da ferramenta proposta.
Foram identificados os seguintes requisitos funcionais para a ferramenta proposta:
• RF01: O sistema deve permitir ao usuário definir as configurações necessárias para se
conectar aos bancos de dados suportados;
75
• RF02: O sistema deve ler as informações sobre as tabelas de um banco de dados ou
schema a partir dos metadados dos sistemas gerenciadores de bancos de dados
suportados;
• RF03: O sistema deve permitir ao usuário selecionar quais tabelas deverão ser utilizadas
para a geração de código;
• RF04: O sistema deve listar ao usuário todos os atributos de cada uma das tabelas
selecionadas para geração de código;
• RF05: O sistema deve permitir ao usuário selecionar quais os atributos, de cada uma das
tabelas selecionadas, deverão ser gerados;
• RF06: O sistema deve permitir ao usuário customizar o nome das classes a serem
geradas;
• RF07: O sistema deve permitir ao usuário customizar o nome dos atributos a serem
gerados;
• RF08: O sistema deve permitir ao usuário definir herança entre as tabelas selecionadas, e
aplicar este conceito para as respectivas classes mapeadas, durante a geração de código;
• RF09: O sistema deve permitir configurar quais os métodos de classe que devem ser
gerados;
• RF10: O sistema deve permitir realizar o mapeamento entre os tipos dos bancos de
dados suportados e os tipos da linguagem de programação escolhida para a geração de
código;
• RF11: O sistema deve permitir criar um projeto;
• RF12: O sistema deve permitir abrir um projeto;
• RF13: O sistema deve permitir salvar um projeto;
• RF14: O sistema deve gerar o código-fonte baseado nas configurações realizadas pelo
usuário e nos arquivos templates selecionados para formatação do código-fonte gerado;
• RF15: O sistema deve permitir que um arquivo ou conjunto de arquivos templates sejam
associados ao projeto;
76
• RF16: O sistema deve permitir que um arquivo ou conjunto de arquivos templates sejam
associados a uma classe ou conjunto de classes;
• RF17: O sistema deve permitir validar a estrutura de atributos de uma classe contra a
estrutura de colunas da tabela mapeada; e
• RF18: O sistema deve permitir excluir uma classe gerada a partir de uma ou mais tabelas
do banco de dados.
Foram identificados os seguintes requisitos não funcionais (com a respectiva categoria
apresentada em parênteses) para o sistema proposto:
• RNF01: O sistema deve ser desenvolvido em linguagem de programação C#
(implementação);
• RNF02: O sistema deve ler os metadados dos bancos de dados Oracle, SQL Server e
PostgreSQL (conformidade);
• RNF03: O sistema deve rodar sobre a plataforma .NET (implementação);
• RNF04: O sistema deve ser uma aplicação Front-End/Desktop (implementação);
• RNF05: O sistema não deve utilizar banco de dados para armazenar as configurações
dos projetos, sendo que tais informações devem ser salvas em XML, de acordo com o
XML Schema definido (implementação);
• RNF06: Os arquivos templates utilizados pelo sistema devem possuir a sintaxe do motor
de templates NVelocity (conformidade); e
• RNF07: O sistema deve utilizar o motor de templates NVelocity (implementação).
Para a ferramenta proposta, foram identificadas as seguintes regras de negócio:
• RN01: As configurações realizadas pelo usuário nas classes a serem geradas, não
modificam o objeto de banco de dados mapeado pela classe;
• RN02: As alterações realizadas manualmente pelo usuário no código-fonte gerado não
são tratadas pelo sistema;
• RN03: Cada classe deve ser mapeada a partir de uma ou mais tabelas do banco de dados;
77
• RN04: Os atributos da classe mapeada devem ter um atributo correspondente na tabela
do banco de dados;
• RN05: Uma classe pode ter nenhum ou vários atributos, onde a quantidade máxima deve
ser igual à quantidade de atributos da tabela correspondente no banco de dados;
• RN06: O processo de geração de código pode utilizar vários arquivos templates, para
gerar diferentes arquivos de código-fonte, baseado no mesmo conjunto de configurações
realizadas pelo usuário;
• RN07: Quando uma classe não possuir um arquivo ou conjunto de arquivos templates
associados a ela, o sistema deve utilizar os arquivos templates associados ao projeto
durante a geração de código;
• RN08: Quando uma classe possuir um arquivo ou conjunto de arquivos templates
associados a ela, o sistema deve ignorar os arquivos templates associados ao projeto e
utilizar apenas os arquivos associados à classe durante a geração de código;
• RN09: Uma tabela do banco de dados pode ser mapeada em várias classes (uma tabela
para toda a hierarquia6);
• RN10: Classes base podem ser mapeadas a partir de atributos comuns de duas ou mais
tabelas do banco de dados (Uma tabela para cada classe concreta da hierarquia7);
• RN11: Um relacionamento entre duas tabelas do banco de dados pode ser mapeado
como generalização e associação ficando a critério do usuário decidir qual a opção
desejada (uma tabela para cada classe da hierarquia8);
• RN12: O sistema deve permitir definir uma classe como sendo classe abstrata;
6 Regra de mapeamento objeto/relacional para mapear hierarquias de herança, onde todos os atributos de todas as classes da hierarquia são mapeados para colunas de uma única tabela no banco de dados (KELLER, 1997, tradução nossa; AMBLER, 2003, tradução nossa; AMBLER, 2004, tradução nossa). 7 Regra de mapeamento objeto/relacional para mapear hierarquias de herança, onde cada classe concreta da hierarquia é mapeada para uma tabela do banco de dados, que contém tanto os atributos da classe concreta quanto os atributos herdados da classe base (KELLER, 1997, tradução nossa; AMBLER, 2003, tradução nossa; AMBLER, 2004, tradução nossa). 8 Regra de mapeamento objeto/relacional para mapear hierarquias de herança, onde cada classe da hierarquia, independentemente de ser concreta ou abstrata, é mapeada para uma tabela do banco de dados. As referências entre as classes concretas e as classes abstratas da hierarquia são implementadas através da utilização de chaves estrangeiras (KELLER, 1997, tradução nossa; AMBLER, 2003, tradução nossa; AMBLER, 2004, tradução nossa).
78
• RN13: A comparação entre atributos de classe e colunas de tabela deve verificar se o
campo mapeado pelo atributo de classe existe na tabela relacionada; e
• RN14: O sistema deve permitir que o usuário defina a extensão e sufixo do arquivo de
código-fonte a nível de templates.
3.2 DIAGRAMA DE CASOS DE USO
De acordo com Booch, Rumbaugh e Jacobson (2005, tradução nossa), os diagramas de caso
de uso permitem especificar o comportamento do sistema que está sendo desenvolvido, sem
especificar como tal comportamento é implementado. Um caso de uso é uma unidade discreta que
descreve a interação entre o sistema e os atores, sendo que o termo ator representa tanto usuários
quanto outros sistemas computacionais (RUMBAUGH; JACOBSON; BOOCH, 2004, tradução
nossa). A Figura 41 apresenta o diagrama de casos de uso da ferramenta proposta.
Figura 41. Diagrama de casos de uso da ferramenta proposta
A Figura 41 mostra a interação entre o usuário (ator) e os casos de uso que constituem o
sistema. O primeiro passo necessário para acessar qualquer funcionalidade da ferramenta é a criação
de um novo projeto, cujos passos necessários são descritos no caso de uso Cria Projeto (UC01).
79
Após ter um projeto criado, o usuário pode manipulá-lo através da interação com os casos de
uso UC03, UC04, UC05, UC06, UC07 e UC10. É desejável permitir ao usuário salvar as
configurações realizadas para posterior continuação. Desta forma, os casos de uso Abre projeto
(UC02) e Salva projeto (UC08), descrevem as ações que o usuário realiza em relação às duas
funcionalidades citadas.
Os passos necessários para a geração do código-fonte das classes mapeadas são definidos a
partir do caso de uso Gera código (UC09), que é responsável por descrever o processo de geração
de código das classes de acesso ao banco de dados.
3.2.1 Cenários e Protótipos de Tela
Um caso de uso descreve um conjunto de fluxos, através dos quais o ator pode interagir.
Cada fluxo é representado por uma seqüência específica de ações que descrevem um
comportamento, denominado cenário. É aconselhável separar o fluxo principal do caso de uso dos
fluxos alternativos, pois geralmente um caso de uso descreve um conjunto de seqüências e não
apenas uma seqüência, sendo impraticável descrever todos os detalhes de um caso de uso complexo
em apenas um cenário (BOOCH; RUMBAUGH; JACOBSON, 2005, tradução nossa).
A prototipação de interfaces objetiva permitir ao usuário um contato direto com a interface,
antes mesmo da finalização do sistema. Esta dinâmica é aconselhável, pois é difícil expressar
requisitos de interface com o usuário através de descrições textuais e diagramas, devido à natureza
dinâmica das interfaces de usuário. Portanto, a prototipação é a maneira mais lógica para
desenvolver interfaces gráficas (SOMMERVILLE, 2007).
Neste projeto, optou-se por utilizar a prototipação das telas para obter-se uma visualização
prévia da interface do software, com o intuito de auxiliar na definição dos requisitos e descrição dos
casos de uso do sistema. Além disso, sabendo da importância de detalhar os cenários de um caso de
uso, apresenta-se no Apêndice A cada caso de uso da ferramenta proposta, com seus respectivos
cenários e relações com os requisitos funcionais, regras de negócio e respectivas telas.
3.3 DIAGRAMA DE CLASSES
Na UML, uma classe descreve um conjunto de objetos que compartilham características
comuns como atributos, operações, relacionamentos e semântica. Tanto a estrutura quanto o
comportamento de um conjunto de objetos pode ser descrito utilizando-se classes, sendo esta o mais
80
importante bloco de construção de qualquer sistema orientado a objetos. Para modelar as classes de
um sistema, utiliza-se um diagrama UML conhecido como diagrama de classes, que representa
graficamente uma visão estática do sistema. Este diagrama é formado basicamente por um conjunto
de classes, interfaces, colaborações e seus relacionamentos, sendo o mais comum dos diagramas
UML utilizados durante a modelagem de um sistema orientado a objetos (RUMBAUGH;
JACOBSON; BOOCH, 2004, tradução nossa; BOOCH; RUMBAUGH; JACOBSON, 2005,
tradução nossa).
Com o intuito de fornecer um melhor entendimento sobre a ferramenta proposta, a Figura 42
mostra o diagrama de classes do sistema e logo em seguida é apresentado um comentário
explicativo sobre cada uma das classes que compõem o diagrama.
81
Figura 42. Diagrama de classes da ferramenta proposta
82
A seguir é apresentada uma breve descrição das classes do diagrama (Figura 42):
• Project: Classe responsável por gerenciar todo o projeto. É através desta classe que se
inicia todo o processo que permite ao usuário configurar as classes a serem geradas;
• DatabaseType: Esta classe define quais são os bancos de dados suportados pela
ferramenta;
• ConnectionsSettings: Classe responsável por gerenciar a configuração de acesso ao
banco de dados, baseando-se no tipo de banco de dados selecionado para armazenar as
informações necessárias para a obtenção do acesso;
• Database: Classe que representa um banco de dados, sendo responsável por gerenciar a
lista de classes que representam as tabelas do banco de dados;
• IStructure: Interface que define a estrutura básica de uma classe que representa uma
estrutura de dados;
• IField: Interface que define a estrutura básica de um campo utilizado por uma estrutura
de dados;
• Table: Classe que representa uma tabela do banco de dados, armazenando informações
como o nome, o owner e a descrição da tabela representada;
• Field: Classe que representa um campo de uma tabela do banco de dados, contendo
informações sobre os metadados do campo representado como seu nome, tipo, entre
outras informações;
• Class: Representa uma classe que deverá ser gerada pelo sistema. A principal
responsabilidade desta classe é gerenciar as propriedades da classe a ser gerada como
seu nome e classe base, além de quais as tabelas que são representadas pela mesma;
• Attribute: Classe responsável por gerenciar as propriedades dos atributos de classe a
serem gerados pelo sistema como o nome do atributo, seu tipo, tamanho, se o mesmo é
nulo, bem como quais os campos das tabelas que são representados pelo atributo de
classe;
• MethodType: Esta classe define quais são os tipos de métodos que podem ser gerados
pelo sistema;
83
• Method: Esta classe representa um método de classe a ser gerado pelo sistema, sendo
atribuição da mesma gerenciar as propriedades do método a ser gerado como seu nome,
escopo, tipo de retorno, entre outros;
• ITemplateOwner: Interface que representa a estrutura básica de uma classe que utiliza
arquivos templates para determinar como será o formato do código-fonte gerado;
• TemplateScope: Define o escopo de um template. Através desta classe é possível definir
que um arquivo template será utilizado para gerar todas as classes do sistema, ou apenas
uma única classe;
• Template: Classe que representa um arquivo template a ser utilizado durante o processo
de geração de código, sendo sua responsabilidade gerenciar o arquivo template
representado;
• Generator: Esta classe é responsável pelo processo de geração de código-fonte, onde
todas as configurações realizadas pelo usuário são cruzadas com os arquivos templates
selecionados, gerando os arquivos que contém o código-fonte das classes configuradas;
• DataTypeMapping: Classe responsável pelos mapeamentos entre tipos de um banco de
dados e tipos de uma linguagem de programação, em específico; e
• DataTypeMappingElement: Classe que representa um mapeamento entre um tipo de
banco de dados e um tipo da linguagem de programação.
Ressalta-se que no diagrama de classes da ferramenta proposta (Figura 42), não são
apresentados os getters e setters utilizados para encapsular os atributos das classes, por motivos de
simplicidade na visualização do diagrama. No entanto, todos os atributos das classes apresentadas
possuem getters e setters para encapsulá-los.
3.4 XML SCHEMA
Em fevereiro de 1998, o W3C (Word Wide Web Consortium) publicou a primeira versão do
documento conhecido como XML Recommendation, que descreve as partes de um documento
XML, seus elementos, atributos, entre outros, além de algumas instruções e declarações para
processamento de tais documentos (W3C, 1998, tradução nossa). Por causa de sua sintaxe simples e
bem definida, analisadores gramaticais (parsers) para XML foram rapidamente desenvolvidos,
84
ajudando o XML a se tornar o formato padrão para transmissão de dados entre diferentes
aplicativos e computadores (BINSTOCK et al, 2002, tradução nossa).
Para que a transmissão de dados em formato XML entre diferentes aplicativos e
computadores seja realizada, é necessário conhecer a estrutura do documento. Isto é possível
através da utilização de um XML Schema, que permite descrever a estrutura de documentos XML.
Esta descrição possibilita definir quais os elementos e atributos que podem ou devem estar
presentes em um documento, especificar o número mínimo e máximo de ocorrências de um
determinado tipo de elemento, descrever um conjunto de valores permitidos para um atributo ou
elemento, definir a ordem de disposição dos elementos, entre outras características de um
documento XML (BINSTOCK et al, 2002, tradução nossa; VLIST, 2002, tradução nossa).
Com base em tais informações, e sabendo que todas as configurações realizadas pelo usuário
na ferramenta como, por exemplo, classes, métodos, atributos, bem como a estrutura das tabelas de
banco de dados relacionado às classes configuradas pelo usuário, serão salvos em formato XML,
torna-se relevante elaborar o XML Schema que documente e valide a estrutura e o conteúdo do
documento XML utilizado pela ferramenta para persistir informações.
Com o intuito de permitir um fácil entendimento, o XML Schema elaborado é apresentado
de maneira gráfica, utilizando a notação da ferramenta Liquid XML Studio. Detalhes sobre cada
estereótipo que compõem a notação utilizada pela ferramenta podem ser obtidos em Liquid (2007).
Além da representação gráfica do XML Schema, o Apêndice B apresenta o código-fonte do XML
Schema mencionado.
A Figura 43 apresenta a visão geral do XML Schema utilizado pela ferramenta proposta para
persistir informações sobre as configurações realizadas.
85
Figura 43. Visão geral do XML Schema da ferramenta proposta
Na Figura 43, onde é apresentada a visão geral do XML Schema proposto, é possível
verificar, de maneira geral, quais os dados que serão salvos no documento XML. O elemento
Project é o nodo raiz do documento XML, contendo informações como o nome do projeto,
mapeamentos entre tipos de bancos de dados e tipos da linguagem de programação e a linguagem
de programação definida pelo usuário para a geração do código-fonte. O elemento Database
armazena as informações básicas do banco de dados, como nome e owner, dados relacionados às
configurações de acesso ao banco de dados e a estrutura das tabelas do banco de dados selecionado.
O elemento Classes possui informações sobre as classes configuradas pelo usuário, como por
exemplo, seus atributos e métodos. Já o elemento Templates possui informações sobre os arquivos
templates definidos para serem utilizados durante o processo de geração de código-fonte. O
elemento Generator possui informações relacionadas às configurações do gerador de código-fonte,
como o diretório onde os arquivos serão gerados e a extensão padrão para tais arquivos.
86
Além da visão geral do XML Schema, é conveniente mostrar os detalhes internos dos
elementos que são constituídos a partir de tipos complexos (complex types). Portanto, a Figura 44,
Figura 45 e Figura 46 apresentam, respectivamente, os detalhes dos elementos
ConfigurationSettings, Table e Field, elemento que pertence a estrutura do elemento Table.
Figura 44. Estrutura do elemento ConfigurationSettings
Figura 45. Estrutura do elemento Table
87
Figura 46. Estrutura do elemento Field
O elemento ConfigurationSettings (Figura 44) descreve as configurações utilizadas para
acesso a um dos bancos de dados suportados pela aplicação. O elemento Table (Figura 45) define as
informações referentes às tabelas do banco de dados como, por exemplo, o nome da tabela, bem
como informações sobre a estrutura dos campos que compõe tal tabela, descritas no elemento Field
(Figura 46).
Além do detalhamento dos elementos ConfigurationSettings e Table, faz-se necessário
descrever a estrutura interna do elemento Class, bem como a estrutura dos demais elementos que
constituem o mesmo. A estrutura de tais elementos pode ser observada na Figura 47, Figura 48,
Figura 49 e Figura 50.
88
Figura 47. Estrutura do elemento Class
Figura 48. Estrutura do elemento Attribute
Figura 49. Estrutura do elemento UnderlyingDBObjects
89
Figura 50. Estrutura do elemento Method
A Figura 47 apresenta os detalhes do elemento Class, que define informações sobre uma
classe configurada pelo usuário, como o seu nome, sua classe base, entre outros. O nome das tabelas
do banco de dados que originaram a classe em questão é definido no elemento Table. Informações
sobre os atributos de classe são definidos no elemento Attribute (Figura 48), e os nomes dos campos
e respectivas tabelas, mapeados pelo atributo de classe, são descritos no elemento
UnderlyingDBObjetcs (Figura 49). A estrutura do elemento Method (Figura 50) descreve as
informações sobre um método de classe. O atributo Type deste elemento define se o método refere-
se a um comando de atualização, exclusão, inserção ou seleção de dados, enquanto que o atributo
Scope determina a visibilidade do método. Já o elemento Parameter descreve um parâmetro
passado para o método.
Por fim, o detalhamento da estrutura interna do elemento Template pode ser observado na
Figura 51.
90
Figura 51. Estrutura do elemento Template
A Figura 51 apresenta os detalhes do elemeto Template, que define informações sobre os
arquivos templates a serem utilizados durante a geração de código-fonte. O atributo Scope
determina se o arquivo template será utilizado a nível de projeto ou a nível de classe. O elemento
Owner é responsável por definir o nome da classe ou do projeto associado ao arquivo template. O
elemento FileName define onde o arquivo template está fisicamente armazenado. Já os elementos
Suffix e FileExtension são utilizados para armazenar características específicas do arquivo de
código-fonte gerado a partir do template, como a extensão do arquivo e um sufixo de identificação.
3.5 PADRÕES DE PROJETOS UTILIZADOS
O objetivo de um padrão é descrever um problema que ocorre várias vezes em nosso
ambiente, para então fornecer a base para a solução deste problema de forma que tal solução possa
ser reutilizada diversas vezes (ALEXANDER, 1977 apud GAMMA et al, 1995, tradução nossa).
Em cada etapa de desenvolvimento podem ser adotados conjuntos de padrões específicos como os
Business Patterns, Analysis Patterns e Design Patterns, utilizados durante as fases de análise de
negócios, análise de sistemas e implementação de sistemas, respectivamente (MAGELA, 2006).
A seguir são descritos os padrões de projeto identificados e utilizados no projeto e
desenvolvimento da ferramenta. Para cada padrão utilizado, apresenta-se seu nome, descrição,
contexto em que foi utilizado no projeto e parte do diagrama de classes UML do projeto que utiliza
tal padrão, quando necessário.
O padrão Model View Controller, conhecido também por MVC, permite separar os objetos
responsáveis pela lógica de negócio e manipulação de dados, dos objetos responsáveis pela visão
(REENSKAUG, 2003, tradução nossa). Este padrão foi aplicado durante todo o desenvolvimento da
91
ferramenta, respeitando o princípio de manter a lógica de negócio e o acesso aos dados separado da
interface de usuário.
Foi utilizado também o padrão Business Object, que de acordo com Alur, Crupi e Malks
(2003, tradução nossa), encapsula e gerencia as regras de negócio, bem como a manipulação e
persistência dos dados. Este padrão foi implementado em todas as classes da aplicação que
necessitaram realizar validações de regras de negócio e/ou persistir dados.
O padrão DAO que, de acordo com Singh et al (2002, tradução nossa), pode ser utilizado
para centralizar e encapsular o acesso a uma fonte de dados, foi utilizado tanto para a obtenção dos
metadados dos bancos de dados suportados pela ferramenta quanto para efetuar a persistência e
recuperação de dados do projeto em XML. Com isto, torna-se mais simples uma futura alteração no
mecanismo de persistência utilizado pela ferramenta, bem como o suporte a novos bancos de dados,
já que as únicas alterações necessárias devem ser realizadas na classes DAO.
A utilização do padrão Transfer Object permite a troca de dados entre os objetos DAO e os
objetos de negócio, sem que um vínculo direto seja estabelecido entre os mesmos (ALUR; CRUPI;
MALKS, 2003, tradução nossa; CRAWFORD; KAPLAN, 2003, tradução nossa). Por tal motivo,
esta padrão foi utilizado na ferramenta, permitindo a transferência de dados entre as camadas da
aplicação sem criar um vínculo direto entre os objetos de cada camada.
A Figura 52 apresenta o diagrama de classes UML que demonstra o modelo de
implementação da classe Project de acordo com os padrões citados anteriormente. Ressalta-se que a
classe Project é representada no diagrama da Figura 52 como ProjectDTO, seguindo o padrão de
nomenclatura de classes conforme o padrão de projeto aplicado. Vale lembrar que não apenas a
classe Project foi implementada de acordo com este modelo, mas todas as principais classes da
aplicação.
92
Figura 52. Diagrama de classes UML representando a implementação das principais classes
Para implementar o mecanismo de validação das regras de negócio e notificação de violação
de tais regras ao usuário, foram utilizados os padrões de projeto Command, Observer e Template
Method. Através da utilização de tais padrões, foi possível criar um modelo de validação e
notificação sem criar uma dependência explícita entre os objetos da aplicação.
O padrão Command permite solicitar a execução de uma operação sem saber exatamente
qual a operação que deve ser executada ou até mesmo sem saber qual o objeto que irá executá-la
(GAMMA et al, 1995, tradução nossa). A utilização deste comando permitiu a criação de um
conjunto de classes responsáveis pela validação das regras de negócio, onde o objeto solicitante não
necessita ter conhecimento sobre o objeto responsável pela validação. Ao receber a solicitação de
validação, a ação a ser executada é delegada para o objeto de negócios responsável.
O padrão Observer, também conhecido como o mecanismo denominado Publish/Subscribe,
define uma dependência entre objetos de um para muitos, de maneira que quando um objeto altera
seu estado, todos os dependentes são notificados e alterados automaticamente (GAMMA et al,
1995, tradução nossa). Este padrão permitiu o envio de notificações à camada de visão sobre as
validações realizadas.
Por final foi utilizado o padrão Template Method que, de acordo com Gamma et al (1995,
tradução nossa), tem como objetivo definir o esqueleto de um algoritmo, delegando alguns passos
do algoritmo para as subclasses. Com isso, as subclasses podem redefinir alguns passos do
algoritmo sem alterar a estrutura do mesmo. A utilização deste padrão permitiu receber a
93
notificação de validação das regras de negócio e avisar os usuários sobre a violação de tais regras.
Para tal, foi criada uma classe base de visão, sendo esta responsável por receber a notificação e
executar o algoritmo padrão de aviso ao usuário. Este algoritmo invoca métodos virtuais que são
implementados pelas subclasses, delegando a estas a responsabilidade de dar as mensagens corretas
ao usuário sobre o problema ocorrido. A Figura 53 mostra o diagrama de classes UML para o
mecanismo de validação e notificação implementados na ferramenta.
Figura 53. Diagrama de classes UML representando o mecanismo de validação e notificação de violações nas regras de negócio
Na Figura 53 pode-se verificar que a classe Validator possui uma coleção de objetos
Validation, sendo que cada Validation é identificado por um nome e referencia a propriedade de
uma classe. Ao ser solicitado para executar uma determinada validação, o objeto Validation dispara
o evento Action, e notifica o objeto de negócio responsável sobre a necessidade de executar uma
determinada validação. Ao finalizar a validação, o objeto de negócio dispara o evento Validated,
notificando a visão sobre o término da validação. Neste momento, o método OnValidated é
executado, invocando os métodos BusinessObjectValidated e SetErrorMappings, implementados
pelas subclasses da visão. Através do método SetErrorMappings, o sistema apresenta as mensagens
de erro usuário de acordo com a regra de negócio que foi violada. Existem ainda outras classes
94
envolvidas no processo, mas estas foram ignoradas no diagrama de classes pelo fato de não serem
de extrema relevância para a solução proposta.
Além dos padrões citados anteriormente, foram utilizados outros padrões como o Composite
View, que permite a criação de uma visão composta por vários componentes de visão mais simples.
Um exemplo de utilização deste padrão pode ser observado na tela principal do sistema. Outro
padrão utilizado foi o Adapter, que permite converter a interface de uma classe em outra interface
esperada por um cliente. Este padrão permitiu transformar várias listas do tipo Dictionary, onde
cada elemento da lista é formado por uma classe que possui uma chave de identificação e um valor,
em listas simples, formadas apenas por valores, suportadas pelos componentes de interface
disponíveis no .NET Framework. Outra utilização para este padrão foi a conversão da interface
utilizada por algumas classes da API MyMeta, em uma interface conhecida pela arquitetura adotada
para a ferramenta.
4 DESENVOLVIMENTO DA FERRAMENTA
Nesta fase, todos os esforços foram concentrados na implementação da ferramenta, que foi
realizada utilizando a linguagem de programação C# e o ambiente de desenvolvimento Microsoft
Visual Studio. Para tal, as atividades de implementação foram divididas em quatro etapas, conforme
apresentado na seção 4.1. Em seguida, na seção 4.2, descreve-se a ferramenta e suas
funcionalidades, e por final a validação da ferramenta demonstrando o funcionamento da mesma.
4.1 IMPLEMENTAÇÃO DA FERRAMENTA
Esta seção tem por objetivo descrever os detalhes de implementação da ferramenta proposta.
A implementação foi dividida em quatro etapas: (i) recuperação dos metadados dos bancos de
dados; (ii) opções de configurações; (iii) geração de código-fonte; e (iv) persistência do projeto. A
seguir, cada uma destas etapas é descrita com mais detalhes.
4.1.1 Recuperação dos Metadados dos Bancos de Dados
Para a recuperação dos metadados dos bancos de dados suportados pela ferramenta
(PostgreSQL, Oracle e SQL Server) foi utilizada a API MyMeta, que demonstrou-se extremamente
viável, de fácil utilização e eficiente.
A arquitetura utilizada pela API, tornou praticamente transparente o tipo de banco de dados
selecionado para recuperação dos metadados, não exigindo muita codificação específica para um
único tipo de banco de dados. A Figura 54 mostra o código necessário para recuperar os metadados
dos três bancos de dados suportados pela aplicação.
96
public void LoadDatabaseMetadata(Dto.Database datab ase) { // Validation MyMeta.dbRoot dbRoot = new MyMeta.dbRoot(); IDatabase metaDatabase = null; // Database connection // Mapping database types and language types // Choose default database #region Reads metadata if (metaDatabase != null) { database.Name = metaDatabase.Name; database.Owner = metaDatabase.SchemaOwner; database.Tables.Clear(); foreach (ITable metaTable in metaDatabase.T ables) { // When dbRoot.driver is equal to Postg reSql, tables from the database information schema are also returned. // In such cases, these tables are not supposed to be added into the table list, because they are // internal tables managed by the datab ase. So, the following conditional statement is to prevent from such cases . if ((database.ConnectionSettings.DBType != Dto.DatabaseType.PostgreSql) || (database.ConnectionSettings.DBType == Dto.DatabaseType.PostgreSql) && !metaTable.Schema.E quals("information_schema")) { Dto.Table table = new Keops.Model.Integration.Dto.Table(database, metaTab le.Name, metaTable.Description); foreach (IColumn metaColumn in meta Table.Columns) { Dto.Field field = new Keops.Model.Integration.Dto.Field(table); field.DefaultValue = metaColumn .Default; field.Description = metaColumn. Description; field.Name = metaColumn.Name; field.Type = metaColumn.DataTyp eName; field.IsAutoKey = metaColumn.Is AutoKey; field.IsForeignKey = metaColumn .IsInForeignKey; field.IsNullable = metaColumn.I sNullable; field.IsPrimaryKey = metaColumn .IsInPrimaryKey; field.MaxLenght = metaColumn.Ch aracterMaxLength; field.NumericPrecision = metaCo lumn.NumericPrecision; field.NumericScale = metaColumn .NumericScale; table.Fields.Add(field); } database.Tables.Add(table); } } } #endregion }
Figura 54. Código-fonte necessário para recuperação de metadados dos bancos de dados suportados
4.1.2 Opções de Configurações
Nesta etapa foram desenvolvidas todas as funcionalidades que possibilitam ao usuário
realizar a configuração das classes a serem geradas. Para fornecer ao usuário uma interface mais
amigável e produtiva, decidiu-se criar uma aplicação em estilo Dockable Windows, que permite ao
usuário a visualização de várias janelas alocadas na janela principal da aplicação. Para tal, foram
utilizados os componentes NetXP 3.0 SP2 (DACRIS, 2008), que fornecem esta funcionalidade.
97
No entanto, com exceção dos componentes já prontos e fornecidos pelo .NET Framework, a
utilização de componentes visuais de terceiros limitou-se aos citados anteriormente. Todos os
outros componentes utilizados para a criação da interface tiveram que ser desenvolvidos.
Ainda nesta etapa do desenvolvimento foi implementada a arquitetura de validação e
notificação das violações ocorridas nas regras de negócio. A Figura 55 mostra o código
implementado na classe Validation (Figura 53) para solicitar a execução do algoritmo de validação
apropriado.
namespace Keops.Model.Business.Validations { /// <summary> /// Represents a validation to be performed. /// </summary> public class Validation { // Attributes #region Events /// <summary> /// The event that is raised when a validat ion must be performed. /// </summary> public event EventHandler<ActionEventArgs> Action; #endregion // Properties #region Methods /// <summary> /// Performs a validation over a specific o bject. /// </summary> /// <param name="obj">The object to be vali dated.</param> /// <returns>Returns false if the object ha s some error; otherwise, returns true.</returns> public bool Validate(object obj){ ActionEventArgs args = new ActionEventA rgs(this.name, this.propertyName); if (Action != null){ Action(obj, args); } this.valid = args.Valid; return this.valid; } #endregion } }
Figura 55. Código-fonte necessário para validação das regras de negócio
4.1.3 Geração de Código-Fonte
Esta etapa do desenvolvimento pode ser considerada como a principal para o funcionamento
da ferramenta. No entanto, com a utilização da API do NVelocity, foi a etapa mais rápida e simples
de ser implementada. O NVelocity atendeu todas as expectativas e possibilitou a criação de um
98
código simples e ao mesmo tempo robusto. A Figura 56 apresenta o código-fonte necessário para a
geração de código-fonte utilizando a API NVelocity.
private static void Generate(Project project, Class c, Dto.Template t) { if (!File.Exists(t.FileName)) { throw new FileNotFoundException(String.Empt y, t.FileName); } // Initializes the NVelocity template engine. VelocityEngine engine = new VelocityEngine(); ExtendedProperties props = new ExtendedProperti es(); FileInfo f = new FileInfo(t.FileName); props.SetProperty("file.resource.loader.path", f.DirectoryName); engine.Init(props); NVelocity.Template template = engine.GetTemplat e(f.Name); // Maps the input values with templates referen ce variables. VelocityContext context = new VelocityContext() ; context.Put("currentClass", c); context.Put("currentProject", project); string targetFileName = GeneratorDao.BuildFullO utputFileName(project, c, t); if (File.Exists(targetFileName)) { File.Delete(targetFileName); } FileStream file = new FileStream(targetFileName , FileMode.OpenOrCreate); StreamWriter writer = new StreamWriter(file); try { // Executes merge. writer.AutoFlush = false; template.Merge(context, writer); writer.Flush(); } finally { if (writer != null) { writer.Close(); writer.Dispose(); writer = null; } if (file != null) { file.Close(); file.Dispose(); file = null; } } }
Figura 56. Código-fonte necessário para geração do código-fonte
Com utilização da API NVelocity, a mais trabalhosa e difícil tarefa foi a criação dos
arquivos templates, que devem seguir o padrão definido pela linguagem VTL e necessitam de
muitos testes e correções até se obter o resultado esperado. A Figura 57 mostra um trecho do
arquivo template utilizado para geras os métodos da classe DAO.
99
public class ${currentClass.Name}DAO { private const string STR_CONN = "$currentProject.Database.ConnectionSettings.Connec tionString"; #foreach($method in $currentClass.Methods) #if ($method.TypeAsString == "Insert" || $method.Ty peAsString == "Update" || $method.TypeAsString == "Delete") #if ($method.TypeAsString == "Insert") #set ($SqlCommand = "INSERT INTO $currentCl ass.TableNames (") #set ($count = 0) #foreach($attribute in $currentClass.Fields .Values) #if ($attribute.IsGenerated) #set ($SqlCommand = "$SqlCommand $a ttribute.FieldName") #set ($count = $count + 1) #if ($count != $currentClass.Fields .Count) #set ($SqlCommand = "${SqlComma nd},") #else #set ($SqlCommand = "${SqlComma nd} ) VALUES (") #end #end #end #set ($count = 0) #foreach($attribute in $currentClass.Fields .Values) #if ($attribute.IsGenerated) #set ($SqlCommand = "$SqlCommand @$ attribute.FieldName") #set ($count = $count + 1) #if ($count != $currentClass.Fields .Count) #set ($SqlCommand = "${SqlComma nd},") #else #set ($SqlCommand = "${SqlComma nd} );") #end #end #end //Continue...
Figura 57. Arquivo template em linguagem VTL utilizado para a geração de código-fonte
4.1.4 Persistência do Projeto
Durante esta etapa do desenvolvimento da aplicação, foram realizadas as implementações
para persistir e recuperar as informações realizadas pelo usuário. O mecanismo de persistência
utilizado para armazenar as configurações realizadas pelo usuário foi o XML. Toda a persistência
foi implementada por objetos DAO, isolando do resto da aplicação os detalhes do mecanismo de
persistência utilizado. Através desta abordagem, torna-se mais fácil alterar futuramente o
mecanismo de persistência utilizado pela ferramenta. A Figura 58 mostra o código-fonte parcial da
implementação do método responsável por salvar o projeto.
100
public static void Save(Project project) { XmlTextWriter doc = new XmlTextWriter(project.F ullFileName, Encoding.UTF8); doc.WriteStartDocument(); // Writes project elements. doc.WriteStartElement("Project"); doc.WriteAttributeString("TargetLanguage", proj ect.TargetLanguage); doc.WriteStartAttribute("DefaultLanguageMapping Index"); doc.WriteValue(project.LanguageTypeMappings.Def aultMappingIndex); doc.WriteEndAttribute(); //Writes project elements... // Writes database elements DatabaseDao.WriteXmlElement(doc, project.Databa se); // Writes class elements. // Writes the end of classes element. doc.WriteEndElement(); // Writes template elements. doc.WriteStartElement("Templates"); // Write template elements // Writes the end of template elements… doc.WriteEndElement(); // Writes generator elements. GeneratorDao.WriteXmlElement(doc, project.Gener ator); // Writes the end of project element. doc.WriteEndElement(); doc.WriteEndDocument(); doc.Close(); }
Figura 58. Código-fonte utilizado para persistir as configurações realizadas no projeto
4.1.5 Testes de Unidade
Durante a fase de implementação da ferramenta, algumas das funcionalidades foram testadas
com o auxílio de testes de unidade. Rocha, Maldonado e Weber (2001) descrevem que o objetivo do
teste de unidade é explorar a menor unidade9 do projeto, buscando encontrar erros de lógica e de
implementação em cada módulo, de forma isolada. A utilização deste tipo de teste ajudou a garantir
a corretude das funcionalidades testadas, mesmo após alterações em estruturas de classes. As
funcionalidades testadas com o auxílio de testes de unidade são mostradas abaixo:
• Leitura de metadados: o objetivo deste teste é verificar o correto funcionamento do
método que realiza a leitura dos metadados dos bancos de dados suportados pela
9 De acordo com o padrão IEEE 610.12-1990, uma unidade é um componente de software que não pode ser subdividido (ROCHA; MALDONADO; WEBER; 2001).
101
ferramenta. Para realizar este teste, primeiramente criou-se uma estrutura de banco de
dados de teste. Depois de ter definida a estrutura do banco de dados, foi codificado o
método do teste unitário. Neste método, invoca-se o método de leitura de metadados e,
em seguida, comparam-se as informações retornadas pelo método com a estrutura
esperada;
• Decodificação de string de conexão: o objetivo deste teste unitário é verificar se o
sistema consegue decompor uma string de conexão de um banco específico e atribuir os
dados da string de conexão dentro da classe responsável pelas configurações de conexão
com o banco de dados;
• Validação de regras de negócio: o objetivo deste teste é verificar o correto
funcionamento do mecanismo implantado para validação das regras de negócio;
• Criptografia de dados: o objetivo deste teste é verificar o correto funcionamento dos
métodos de criptografia de dados utilizado pela ferramenta;
• Comparação de estruturas: o objetivo destes testes é verificar o correto funcionamento
do método que realiza a comparação entre a estrutura de uma tabela do banco de dados
com a estrutura de uma classe que mapeia tal tabela, verificando se ocorreram alterações
na estrutura da tabela ou da classe; e
• Mapeamento de tipos: o objetivo destes testes unitários é verificar o correto
funcionamento dos métodos responsáveis por importar e exportar os mapeamentos de
tipos realizados na ferramenta.
A Figura 59 mostra a lista de testes de unidade utilizados durante o desenvolvimento da
ferramenta (Figura 59-A) e o resultado da execução de tais testes (Figura 59-B), sendo que tanto
para a criação quanto para a execução de tais testes foram utilizados os recursos fornecidos pela
ferramenta Microsoft Visual Studio.
102
Figura 59. Lista dos testes de unidade utilizados para validação da ferramenta e o resultado da execução de tais testes
4.2 FERRAMENTA PROPOSTA - KEOPS
O objetivo desta seção é descrever a ferramenta proposta, denominada Keops, suas
funcionalidades, e demonstrar através de um estudo de caso a utilização da ferramenta. Para tal, a
seção 4.2.1 apresenta as funcionalidades da ferramenta Keops, enquanto que a seção 4.2.2
demonstra o uso da ferramenta através de um estudo de caso que demonstra os passos necessários
para gerar o código-fonte desejado.
4.2.1 Funcionamento da Ferramenta
O objetivo da ferramenta é permitir a geração de código-fonte para a camada Modelo do
padrão MVC baseado nas tabelas existentes de um banco de dados. Para atingir este objetivo, o
usuário deve seguir os seguintes passos:
1. Recuperação dos metadados das tabelas do banco de dados: este passo se dá informando
os dados necessários para efetuar a conexão ao banco de dados desejado;
103
2. Configuração das classes a serem geradas: neste passo, o usuário seleciona e configura
as classes que deseja gerar. Através desta opção, o usuário pode selecionar e configurar
atributos de classe, bem como definir os métodos que devem ser gerados. Ainda nesta
etapa, o usuário configura quais os arquivos templates deverão ser utilizados durante a
geração do código-fonte; e
3. Geração do código-fonte: por fim, o usuário gera o código-fonte para as classes
desejadas, tendo como base as configurações realizadas e os arquivos templates
selecionados.
A Figura 60 mostra os passos apresentados anteriormente através de um diagrama de
atividades.
Figura 60. Diagrama de atividades para a ferramenta Keops
4.2.2 Demonstração da Ferramenta
Com o intuito de verificar o funcionamento e a corretude da ferramenta, foi desenvolvido
um estudo de caso que compreende a criação de um sistema para cadastro de trabalhos de conclusão
de curso. Esta demonstração objetiva apresentar o funcionamento da ferramenta através de um
passo-a-passo utilizando a ferramenta desenvolvida.
No contexto do estudo de caso desenvolvido, um aluno formando deve realizar um trabalho
de conclusão de curso, sendo que tal trabalho deve ser orientado por um professor. Para tal, foi
criado um modelo de entidade e relacionamento, apresentado na Figura 61, que demonstra as
tabelas de banco de dados necessárias para o estudo de caso em questão.
104
Figura 61. Modelo de entidade e relacionamento do estudo de caso
O modelo apresentado na Figura 61 foi implementado no banco de dados PostgreSQL,
permitindo a demonstração do estudo de caso. No entanto, o mesmo poderia ter sido implementado
em qualquer um dos outros dois bancos suportados pela ferramenta, SQL Server ou Oracle. A
Figura 62 apresenta os comandos SQL utilizados para a criação das tabelas do modelo de entidade e
relacionamento do estudo de caso.
105
CREATE TABLE aluno ( id integer NOT NULL, nome varchar(50) NOT NULL, cpf varchar(11) NULL, CONSTRAINT PK_Aluno PRIMARY KEY (id)); CREATE TABLE professor ( id integer NOT NULL, nome varchar(50) NOT NULL, cpf varchar(9) NULL, CONSTRAINT PK_Professor PRIMARY KEY (id)); CREATE TABLE tcc ( id integer NOT NULL, titulo varchar(50) NOT NULL, id_aluno integer NULL, id_professor integer NULL, CONSTRAINT PK_tcc PRIMARY KEY (id), CONSTRAINT FK_tcc_aluno FOREIGN KEY (id_aluno) REFERENCES aluno (id), CONSTRAINT FK_tcc_professor FOREIGN KEY (id_pro fessor) REFERENCES professor (id));
Figura 62. Comandos SQL para criação das tabelas do estudo de caso
Após ter o banco de dados criado, pode-se iniciar a demonstração da ferramenta através da
criação de um novo projeto. Na criação de um novo projeto é necessário definir um nome para o
mesmo, informar qual o diretório físico em que o projeto ficará localizado e o banco de dados a ser
utilizado, conforme apresentado na Figura 63.
Figura 63. Tela para a criação de um novo projeto
Após a criação do projeto, o usuário pode iniciar a configuração do projeto. A primeira
configuração a ser realizada é o mapeamento entre tipos do banco de dados e tipos da linguagem
utilizada para geração de código-fonte. Nesta fase o usuário pode incluir, alterar ou excluir
mapeamentos entre tipos do banco de dados e tipos da linguagem de programação conforme sua
necessidade, através das funcionalidades denominadas “Adicionar”, “Editar” e “Remover”. A
Figura 64 mostra a tela em que o usuário pode realizar as configurações de mapeamentos de tipos.
106
Figura 64. Mapeamento entre tipos do banco de dados e da linguagem de programação
Com o mapeamento criado, o usuário pode definir as configurações gerais de geração de
código-fonte do projeto. Para isso, ele deve definir quais os mapementos padrões que o projeto deve
utilizar, bem como o diretório e extensão padrão dos arquivos de código-fonte gerados. A Figura 65
e a Figura 66 apresentam a configuração dos mapeamentos de tipos padrão e as configurações de
geração de código-fonte, respectivamente, utilizadas pelo projeto.
Figura 65. Configuração dos mapeamentos de tipos padrão
107
Figura 66. Configurações padrões para geração de código-fonte
Após configurar as opções gerais do projeto, pode-se realizar a importação dos metadados
do banco de dados. Para que a conexão seja estabelecida, é necessário informar os dados
necessários para conectar ao banco de dados. As informações necessárias variam de acordo com o
tipo de banco de dados utilizado, sendo que a ferramenta solicita ao usuário apenas o preenchimento
dos campos necessários conforme o banco de dados selecionado. A Figura 67 apresenta a tela onde
são informados os dados para estabelecimento de conexão com o banco de dados PostgreSQL.
Figura 67. Configuração dos dados para conexão com o banco de dados
108
Após ser estabelecida a conexão com o banco de dados, o sistema apresenta as tabelas
existentes no banco de dados. A Figura 68 apresenta as tabelas existentes no banco de dados do
estudo de caso.
Figura 68. Tabelas existentes no banco de dados do estudo de caso
Uma vez que as tabelas disponíveis no banco de dados foram importadas para o projeto,
pode-se iniciar a configuração das classes a serem geradas. Para isso, deve-se definir a arquitetura
das classes a serem geradas. Neste estudo de caso, poderiam ser geradas três classes mapeadas
diretamente a partir das tabelas do banco de dados. No entanto, pode-se também definir que as
tabelas aluno e professor podem gerar uma classe base e a partir desta classe, gerar as outras duas
classes que mapeiam tais tabelas. A ferramenta proposta oferece a opção de criar esta classe base.
Para isso, deve-se selecionar as tabelas desejadas, neste caso aluno e professor, clicar com o botão
direito do mouse sobre uma das tabelas selecionadas e escolher a opção “Criar classe base”. Ao
realizar esta ação, o sistema permite que o usuário configure a classe base a ser gerada, conforme
apresentado na Figura 69.
109
Figura 69. Criação de classe base a partir de duas ou mais tabelas do banco de dados
A estrutura do banco de dados implementada para este estudo de caso utiliza duas tabelas
distintas para armazenar registros de alunos e professores. No entanto, outra opção seria criar uma
tabela única e utilizar um atributo que identificaria o registro como sendo um aluno ou um
professor. Para casos como este, a ferramenta oferece a alternativa de decompor uma tabela do
banco de dados em várias classes, para que uma hierarquia de classes possa ser constituída. Para
obter este resultado, o usuário deve selecionar a tabela que deseja decompor, clicar com o botão
direito do mouse e selecionar a opção “Decompor tabela”. Em seguida, deve realizar as
configurações necessárias para obter a hierarquia desejada.
Após a criação da hierarquia de classes formada por Pessoa, Aluno e Professor, falta ainda
criar a classe Tcc. Neste caso, a classe Tcc será mapeada diretamente a partir da tabela tcc. Para
criar uma classe a partir de uma única tabela do banco de dados, o usuário deve selecionar a tabela
desejada, neste caso a tabela tcc, clicar com o botão direito do mouse e escolher a opção “Gerar
classe”. A Figura 70 apresenta a lista de objetos do projeto, contendo tanto as tabelas do banco de
dados quanto as classes geradas a partir de tais tabelas.
110
Figura 70. Lista de objetos do projeto contendo as tabelas do banco de dados e as classes geradas
Tendo disponível a lista de classes a serem geradas, torna-se possível configurar as
propriedades, atributos e métodos destas classes. Para isso, deve-se selecionar a classe desejada,
clicar com o botão direito do mouse e escolher a opção “Editar”. O sistema apresentará uma tela
que permite configurar as propriedades, atributos e métodos da classe, conforme exibido na Figura
71.
Figura 71. Configuração da classe Tcc
Na Figura 71 observa-se as configurações da classe Tcc. Salienta-se a configuração dos
atributos “aluno” e “professor”, cujo tipo foi configurado para referenciar as classes “Aluno” e
“Professor”.
Outra configuração importante, que não foi utilizada na classe Tcc, mas é necessária na
configuração das classes “Aluno” e “Professor”, é a opção disponibilizada pelo campo “Classe
111
base”. Através deste campo, o usuário pode configurar qual a classe base de uma classe filha. Para
realizar esta configuração, o usuário deve definir o campo “Classe base” com o nome da classe base
desejada. Se o usuário abrir a lista de valores do combo box no campo “Classe base”, serão exibidas
todas as classes do projeto, facilitando a seleção da classe base. Neste estudo de caso, foi realizada
esta configuração nas classes “Aluno” e “Professor”, definindo o campo classe base com o valor
“Pessoa”. Outro ponto importante para a correta geração do código-fonte que envolve hierarquias
de classes, é desmarcar para geração de código todos os atributos das classes filhas que estão
configuradas na classe base. Neste exemplo, devem ser desmarcados os atributos “Id”, “Nome” e
“Cpf” nas classes “Aluno” e “Professor”, já que os mesmos devem ser configurados na classe base
“Pessoa”. Isto se faz necessário, pois como tais classes herdam os atributos da classe Pessoa, estes
mesmos atributos não devem ser configurados e gerados novamente nas classes filhas.
Além dos atributos da classe, devem ser configurados também os métodos a serem gerados.
Para atender este objetivo, o usuário deve acessar a aba “Métodos” disponível na tela de
configuração de classe (Figura 71) e clicar no botão “Adicionar”. Em seguida, deve configurar o
método desejado. Durante a configuração do método, o usuário deve definir o nome para o método,
selecionar o tipo de retorno e a visibilidade do método, bem como determinar os parâmetros e o
comando SQL que deve ser executado pelo método. Ao selecionar o tipo do método, a ferramenta
apresenta ao usuário o comando SQL padrão para o tipo de método escolhido. No entanto, o usuário
pode alterar o comando SQL, caso desejado. A Figura 72 exibe a tela de configuração de métodos
de classe.
112
Figura 72. Configuração de método de classe
Após realizar todas as configurações nas classes que devem ser geradas, o usuário deve
definir quais arquivos templates devem ser utilizados durante o processo de geração de código-
fonte. Os templates podem ser associados tanto ao projeto quanto a uma classe ou conjunto de
classes específicas. Com isso, é possível obter uma geração de código-fonte personalizada para
alguma classe específica. Para associar um arquivo template ao projeto, o usuário deve utilizar a
opção de menu “Ferramentas” e em seguida escolher a opção “Gerenciador de templates”. Para
associar um arquivo template a uma classe ou conjunto de classes, o usuário deve selecionar as
classes desejadas, clicar com o botão direito do mouse sobre uma das classes selecionadas e
selecionar a opção “Gerenciador de templates”. Depois de executado um dos passos descritos
anteriormente, o sistema apresenta uma tela, conforme exibido na Figura 73, para definir os
arquivos templates a serem utilizados durante a geração de código-fonte. Neste estudo de caso,
113
optou-se por utilizar um conjunto de arquivos templates para todo o projeto, ou seja, durante o
processo de geração de código-fonte, todas as classes serão geradas com base no mesmo conjunto
de arquivos templates.
Figura 73. Configuração dos arquivos templates a serem utilizados durante o processo de geração
Para definir os arquivos templates que devem ser utilizados durante a geração de código-
fonte, o usuário deve clicar no botão “Adicionar” e selecionar os arquivos templates desejados.
Pode ocorrer de o usuário definir mais de um arquivo template para a mesma classe. Nestes casos,
deve-se definir um sufixo a ser utilizado para nomeação do arquivo de código-fonte a ser gerado,
caso contrário, os arquivos serão sobrescritos. Isto pode ser útil para casos em que o usuário utiliza
vários arquivos templates para gerar classes utilizando padrões de projeto como o DAO, Data
Transfer Object e Business Object, e queira definir para cada padrão um sufixo a ser utilizado na
nomeação dos arquivos gerados, como AlunoDAO, AlunoDTO, etc. Outra opção disponível ao
usuário é configurar a extensão do arquivo de código-fonte gerado a nível de template. Isto pode ser
útil caso alguma linguagem de programação utilize diferentes extensões de arquivos, dependendo
do tipo de código-fonte contido no arquivo. O código dos arquivos templates utilizados neste estudo
de caso para gerar as classes DTO e DAO encontram-se nas seções C.1 e C.2, respectivamente, do
Apêndice C.
Após realizar todas as configurações das classes e definir quais os arquivos templates que
devem ser utilizados, resta apenas gerar o código-fonte. A ferramenta permite que o usuário realize
114
a geração de todas as classes do projeto, ou selecione quais as classes que devem ser geradas. Para
gerar todas as classes do projeto, o usuário deve acessar a opção de menu “Ferramentas” e
selecionar a opção “Gerar código-fonte”. Caso deseje gerar apenas o código-fonte de uma classe ou
conjunto de classes, o usuário deve selecionar as classes desejadas, clicar com o botão direito do
mouse sobre uma das classes selecionadas e escolher a opção “Gerar código-fonte”. O sistema
apresentará ao usuário uma tela contendo um botão “Gerar”. Ao clicar neste botão, o sistema irá
gerar o código-fonte das classes do projeto ou das classes selecionadas para geração, conforme
opção definida pelo usuário. Neste estudo de caso, optou-se por gerar todas as classes do projeto. A
Figura 74 apresenta a tela de geração de código-fonte, após a finalização do processo de geração.
Figura 74. Geração de código-fonte das classes do projeto
Durante a geração do código-fonte, o sistema apresenta ao usuário o status da geração de
cada um dos arquivos de código-fonte. Caso algum erro ocorra durante este processo, o sistema
apresenta ao usuário os detalhes do erro ocorrido. Caso deseje, o usuário pode salvar o log do
processo de geração de código-fonte clicando no botão “Salvar log”. A Figura 75 apresenta o
diagrama de classes para as classes geradas neste estudo de caso.
115
Figura 75. Diagram de classes das classes geradas pela ferramenta Keops
No diagrama de classes apresentado na Figura 75, observam-se as classes geradas pela
ferramenta Keops a partir das configurações realizadas e dos arquivos templates definidos no estudo
de caso. As classes relacionadas ao padrão DAO foram respectivamente nomeadas com o sufixo
DAO, enquanto que as classes relacionadas ao padrão Data Transfer Object mantiveram o nome da
classe configurado pelo usuário na ferramenta.
Alguns dos métodos presentes nas classes DAO do diagrama de classes não foram
exemplificados durante o estudo de caso para evitar a repetição de passos. No entanto, foi descrito
como configurar um método através da configuração do método “SelecionarPorId” da classe
TccDAO, sendo utilizado o mesmo conjunto de instruções para configurar os outros métodos.
As classes relacionadas ao Tcc, ou seja, Tcc e TccDAO, foram completamente geradas,
contendo todos os métodos de manipulação de dados necessários para demonstrar o funcionamento
da ferramenta. Já para as classes da hierarquia PessoaDAO, optou-se por não gerar todos os
métodos de manipulação de dados como inserção, exclusão e alteração, sendo gerado apenas o
método “SelecionarPorId”, pois o mesmo é utilizado no arquivo de código-fonte da classe
116
TccDAO. O código-fonte gerado pela ferramenta, em linguagem de programação C#, para as
classes demonstradas no diagrama de classes (Figura 75) pode ser observado no Apêndice E.
4.2.2.1 Validação do Código-Fonte Gerado
Com o intuito de verificar a corretude do código-fonte gerado pela ferramenta, foi criado no
Microsoft Visual Studio um projeto do tipo Library, que é basicamente uma biblioteca de classes
que possibilita a reutilização de tais classes em outros projetos, e em seguida importado os arquivos
gerados pela ferramenta para este projeto, denominado StudyCaseDAO. Após a realização dos
passos descritos anteriormente, foi realizada a compilação do código-fonte, cujo resultado pode ser
observado no canto inferior esquerdo da Figura 76.
Figura 76. Resultado da compilação do código-fonte gerado
No entanto, para garantir que o código-fonte gerado realmente funciona, e não apenas
compila, foi criado um projeto que possibilita a validação do código-fonte gerado. Em seguida, foi
importado para este projeto a biblioteca de classes criada anteriormente com o código-fonte gerado
117
pela ferramenta, o projeto StudyCaseDAO. Por último, foi criada uma interface de usuário que
permite cadastrar e consultar TCCs, conforme apresentado na Figura 77.
Figura 77. Interface de validação do código-fonte gerado
A codificação necessária para a tela demonstrada na Figura 77 envolveu apenas chamadas à
biblioteca de classes importada, que contém o código-fonte gerado pela ferramenta. A Figura 78
mostra a codificação utilizada para cadastrar um novo TCC baseado nas informações preenchidas
na tela, sendo tal código invocado ao clicar sobre o botão “Salvar”.
118
Tcc to = new Tcc(); Aluno alunoTO = new Aluno(); Professor professorTO = new Professor(); alunoTO.Id = Int32.Parse(txtStudentId.Text); professorTO.Id = Int32.Parse(txtProfessorId.Text); to.Id = Int32.Parse(txtId.Text); to.Titulo = txtDescription.Text; to.Aluno = alunoTO; to.Professor = professorTO; TccDAO dao = new TccDAO(); try { dao.Inserir(to); } catch (Exception ex) { MessageBox.Show(String.Format("Erro ao inserir. \n{0}", ex.Message)); }
Figura 78. Código-fonte para cadastrar um TCC
Conforme pode ser observado na Figura 78, não é necessário conhecer nenhum detalhe do
mecanismo de persistência utilizado para armazenar os registros no mecanismo de persistência, pois
são realizadas apenas chamadas aos métodos das classes geradas, neste caso a classe TccDAO,
sendo de responsabilidade desta classe realizar a inserção do registro no banco de dados. Baseado
em tal projeto de validação, tornou-se possível comprovar a corretude do código-fonte gerado pela
ferramenta.
4.2.2.2 Validação de Geração de Código-Fonte para Várias Linguagens de Programação
Visando a garantia de que a ferramenta Keops pode ser utilizada para gerar código-fonte
para diferentes linguagens de programação através da utilização de arquivos templates, foi realizado
um estudo de caso semelhante ao demonstrado na seção 4.2.2. A única diferença entre os mesmos
ocorre na configuração das opções de mapeamento de tipos e geração de código-fonte, conforme
demonstrado na seção 4.2.2, Figura 65 e Figura 66, respectivamente, pois o objetivo deste estudo de
caso é a geração de código-fonte para a linguagem de programação VB.NET. Na opção de
mapeamento de tipos (Figura 65), foi configurado o mapeamento de tipos entre o banco de dados
PostgreSQL e VB.NET, ao invés de PostgreSQL e C#, e na opção de geração de código-fonte
(Figura 66) foi alterada a extensão padrão dos arquivos gerados para “.vb”.
Em seguida, foi realizado o passo demonstrado na Figura 73, que se refere à configuração
dos arquivos templates a serem utilizados durante o processo de geração do código-fonte. Neste
caso, foram selecionados arquivos templates que geram código-fonte para a linguagem de
programação VB.NET. O código dos arquivos templates utilizados neste estudo de caso para gerar
as classes DTO e DAO encontram-se nas seções D.1 e D.2, respectivamente, do Apêndice D e o
119
código-fonte gerado pela ferramenta, em linguagem de programação VB.NET, para as classes
demonstradas no diagrama de classes (Figura 75) pode ser observado no Apêndice F.
Após a geração do código-fonte em VB.NET, foi realizada a mesma validação do código-
fonte gerado demonstrado na seção 2.2.2.1, ou seja, foi criado no Visual Studio um projeto do tipo
Library, efetuada a importação dos arquivos gerados em VB.NET e por fim efetuada a compilação
da biblioteca de classes. Em seguida, esta biblioteca de classes foi importada para o projeto de
validação de código-fonte, e realizado o cadastro e consultas de TCCs, de acordo com as
funcionalidades fornecidas pela interface de usuário demonstrada na Figura 77.
120
5 CONSIDERAÇÕES FINAIS
O principal objetivo deste projeto é fornecer uma ferramenta que seja capaz de contribuir
para o ciclo de desenvolvimento de software através da geração de código-fonte automatizada para
qualquer linguagem de programação baseada em texto e levando em consideração os metadados de
um banco de dados existente.
Durante o desenvolvimento da ferramenta proposta, foram utilizados diversos padrões de
projeto, entre eles o MVC e o DAO, sendo possível constatar alguns dos benefícios fornecidos por
tais padrões. Entre tais benefícios destaca-se a separação da camada modelo do resto da aplicação,
conforme descrito por Metsker (2002, tradução nossa) e Fowler (2002, tradução nossa), a redução
de complexidade dos objetos de negócio e o encapsulamento das características do mecanismo de
persistência utilizado, conforme citado por Alur, Crupi e Malks (2003, tradução nossa) e Marinescu
(2002, tradução nossa).
Um dos pontos marcantes, que a princípio poderia até ser interpretado como desvantagem
em utilizar padrões de projeto, é a morosidade no início do processo de desenvolvimento, já que
foram necessárias análises mais aprofundadas e algumas vezes até maior quantidade de código para
implementar alguma funcionalidade utilizando padrões de projeto. No entanto, a idéia de
desvantagem certamente foi dispersa ao longo do desenvolvimento, quando foram necessárias
algumas pequenas alterações na arquitetura inicialmente projetada, mas especificamente em como
codificar a persistência e recuperação das configurações realizadas pelo usuário em XML.
Com o auxílio de testes de unidade, que foram utilizados para testar algumas das principais
funcionalidades da ferramenta, foi possível comprovar outra vantagem do padrão DAO, citada por
Alur, Crupi e Malks (2003, tradução nossa), que é a possibilidade de desenvolver e testar a camada
de acesso a dados separadamente do resto da aplicação, conforme explanado na seção 4.1.5. Além
disso, os testes de unidade também auxiliaram a garantir o correto funcionamento das unidades de
código após serem realizadas alterações em alguns pontos da arquitetura que poderiam impactar em
toda a aplicação, citando como exemplo o mecanismo de validação e notificação implementado
para validar as regras de negócio da aplicação.
O padrão DAO também foi útil para encapsular os detalhes de acesso aos bancos de dados
suportados, tornando a recuperação dos metadados totalmente transparente para o resto da
121
aplicação. Outro recurso muito importante, foi a utilização da API MyMeta, que facilitou e
simplificou a forma de recuperar os metadados dos bancos de dados suportados, PostgreSQL,
Oracle e SQL Server.
O desenvolvimento da interface gráfica da ferramenta, que possibilita ao usuário a
configuração das classes a serem geradas, foi facilitado com o auxílio dos componentes visuais já
fornecidos pelo .Net Framework e pela utilização dos componentes NetXP 3.0 SP2, que
possibilitaram a criação de uma interface em estilo Dockable Windows, além de permitir que o
usuário realize a configuração de vários classes ao mesmo tempo apenas navegando pelas abas
abertas na aplicação, sem a necessidade de abrir e fechar janelas para configurar as classes.
A princípio esperava-se que a codificação necessária para realizar a geração de código-fonte
baseado nas configurações realizadas e nos templates definidos pelo usuário, seria a etapa mais
crítica e complexa do processo de desenvolvimento. Esta fase certamente foi a mais crítica, já que
um dos principais objetivos da ferramenta, gerar código-fonte para qualquer linguagem de
programação baseada em texto, dependia totalmente do sucesso na implementação desta fase.
No entanto, a idéia de complexidade foi totalmente eliminada com a utilização do motor de
templates NVelocity. Este motor de templates utiliza a linguagem denominada VTL para configurar
os arquivos templates, que determinam como será gerada a saída. Através do uso do NVelocity, foi
possível realizar a geração de código-fonte para qualquer linguagem de programação com
simplicidade e facilidade, mas de maneira robusta. Com a utilização do NVelocity, a maior
dificuldade encontrada nesta etapa do desenvolvimento foi a criação dos arquivos templates. Isto
ocorreu devido à falta de um depurador para a linguagem VTL, resultando na necessidade de vários
testes e correções nos arquivos templates até obter-se o resultado desejado.
Através da utilização da ferramenta, espera-se trazer benefícios para o ciclo de
desenvolvimento de software, automatizando o processo de geração de código-fonte para
arquiteturas bem definidas, como é o caso das camadas de acesso a dados, permitindo que os
desenvolvedores mantenham o foco em pontos mais desafiadores da aplicação, como a codificação
das regras de negócio. A característica da ferramenta em permitir a geração para qualquer
linguagem de programação baseada em texto, é outro ponto fundamental que pode beneficiar as
equipes de desenvolvimento.
122
5.1 TRABALHOS FUTUROS
Durante o planejamento e desenvolvimento da ferramenta foram identicados recursos e
funcionalidades que poderiam ser incluídas na ferramenta desenvolvida, mas que por estarem fora
do escopo inicial do projeto, não foram implementadas durante o desenvolvimento desta proposta.
No entanto, estes recursos e funcionalidades são elencados a seguir com o intuito de orientar futuros
trabalhos que tenham como objetivo melhorar e ampliar a ferramenta desenvolvida, aumentando a
robustez da mesma.
Entre os recursos desejáveis à ferramenta, destaca-se o suporte a outros sistemas
gerenciadores de bancos de dados, como MySQL, Firebird, Microsoft Access, IBM DB2,
Pervasive, SQLite, entre outros. O suporte a outros sistemas gerenciadores de banco de dados
trariam à ferramenta a possibilidade de ser utilizada em mais ambientes de desenvolvimento.
Além de aumentar o suporte a outros bancos de dados, sugere-se também o suporte à
modelos de classes no formato XMI, que ao serem importados para a ferramenta, forneceria a base
para a geração do código-fonte. No entanto, acredita-se que a possibilidade de importar modelos no
formato XMI seria uma alternativa não-exclusiva, mantendo a atual funcionalidade de importar os
metadados de bancos de dados existentes.
Pensando-se na utilização da ferramenta por equipes de desenvolvimento com maior número
de colaboradores, percebe-se também a possibilidade de permitir que a persistêcia das
configurações realizadas pelo usuário seja realizada também em um banco de dados. Desta forma, o
mesmo projeto poderia ser utilizado de modo simultâneo, característica impossível com a utilização
de arquivos XML. No entanto, novamente, esta funcionalidade deveria ser um adicional à
ferramenta, mantendo a funcionalidade atual de persistir o projeto em XML.
Ampliando o escopo da ferramenta, seria interessante que a ferramenta oferecesse suporte à
geração das demais camadas do MVC. Logo, uma funcionalidade que permitisse o mapeamento de
atributos de classe a componentes de interface gráfica de usuário, seria um exemplo de suporte a
geração da camada de visão do MVC. Obviamente, outras funcionalidades adicionais podem ser
estudadas e analisadas para que seja possível também gerar completamente ou parte de todas as
camadas do padrão MVC.
123
Outros recursos podem ainda ser adicionados à ferramenta com o intuito de aumentar sua
robustez e ampliar o seu uso, como o suporte a arquivos templates com sintaxe semelhante ao
ASP.NET e a possibilidade de editar os arquivos templates dentro da própria ferramenta.
124
REFERÊNCIAS BIBLIOGRÁFICAS
ALUR, D.; CRUPI, J.; MALKS, D. Core J2EE patterns: best practices and design strategies. 2.ed. New Jersey: Prentice Hall, 2003.
AMBLER, S. W. Agile database techniques: effective strategies for the Agile software developer. Indianapolis: Willey Publishing, 2003.
AMBLER, S. W. The object primer: Agile model-driven development with UML 2.0. 3.ed. New York: Cambridge University Press, 2004.
ANTTILA, A. C# data tier generator. [S.l.: s.n.], 2001. Disponível em: < http://sourceforge.net/projects/csharpdatatier/ >. Acesso em: 03 nov. 2007.
APACHE. Velocity. [S.l.], 2007a. Disponível em: < http://velocity.apache.org/engine/releases/velocity-1.5/index.html >. Acesso em: 21 jul. 2008.
APACHE. Velocity user guide. [S.l.], 2007b. Disponível em: < http://velocity.apache.org/engine/releases/velocity-1.5/user-guide.html >. Acesso em: 21 jul. 2008.
APACHE. VTL reference. [S.l.], 2007c. Disponível em: < http://velocity.apache.org/engine/releases/velocity-1.5/vtl-reference-guide.html >. Acesso em: 21 jul. 2008.
AVERY, J.; HOLMES, J. Windows developer power tools: turbocharge Windows development with 140+ free tools. Sebastopol: O’Reilly, 2006.
BINSTOCK, C. et al. The XML schema complete reference. Boston: Addison-Wesley, 2002.
BISHOP, J. C# 3.0 design patterns. Sebastopol: O’Reilly, 2008.
BOOCH, G., et al. Object-oriented analysis and design with applications. 3.ed. Boston: Addison-Wesley, 2007.
BOOCH, G., RUMBAUGH, J., JACOBSON, I. The unified modeling language user guide. 2.ed. Boston: Addison-Wesley, 2005.
BORRIE, H. The Firebird book: a reference for database developers. New York: Apress, 2004.
BROEMMER, D. J2EE best practices: Java design, patterns, automation and performance. Indianapolis: Willey Publishing, 2003.
BUSCHMANN, F., et al. Pattern-oriented software architecture: a system of patterns. Indianapolis: Willey Publishing, 1996. 1 v.
BUSCHMANN, F.; HENNEY, K.; SCHMIDT, D. C. Pattern-oriented software architecture: on patterns and pattern languages. Indianapolis: Willey Publishing, 2007. 5 v.
125
CASTILHOS, C. Ferramenta CASE para geração de páginas ASP. 2004. 74 f. Trabalho de Conclusão de Curso (Graduação em Ciências da Computação) – Centro de Ciências Exatas e Naturais, Universidade Regional de Blumenau, Blumenau, 2004.
CASTLEPROJECT. Using NVelocity. [S.l.], [2007]. Disponível em: < http://www.castleproject.org/others/nvelocity/index.html >. Acesso em: 21 jul. 2008.
CLEAVELAND, J. C. Building application generators. [S.l.]: IEEE, 1988. Disponível em: < http://ieeexplore.ieee.org/iel1/52/651/00017799.pdf >. Acesso em: 25 maio 2008.
CODEFUTURES. FireStorm/DAO . [S.l.], 2007. Disponível em: < http://www.codefutures.com/products/firestorm/ >. Acesso em: 03 nov. 2007.
CODESMITH. Code Smith tools. [S.l.], 2007a. Disponível em: < http://www.codesmithtools.com >. Acesso em: 03 nov. 2007.
CODESMITH. CodeSmith user’s guide. Versão 4.1. Dallas: CodeSmith, 2007b. Arquivo Help. Disponível em: < http://www.codesmithtools.com >. Acesso em: 03 nov. 2007.
COELHO, L. F. Gerador de código HTML baseado em dicionário de dados utilizando banco de dados. 2006. 52 f. Trabalho de Conclusão de Curso (Graduação em Ciência da Computação) – Centro de Ciências Exatas e Naturais, Universidade Regional de Blumenau, Blumenau, 2006.
CODD, E. F. The relational model for database management. 2.ed. Boston: Addison-Wesley, 1990.
COOPER, J. W. C# design patterns: a tutorial. Boston: Addison-Wesley, 2003.
CRAWFORD, W., KAPLAN, J. J2EE design patterns. Sebastopol: O’Reilly, 2003.
DACRIS. NetXP 3.0 SP2. [S.l.], 2008. Disponível em: < http://www.dacris.com/downloads/detail.aspx?id=12 >. Acesso em: 02 nov. 2008.
DALGARNO, M. Frequently asked questions about code generations. [S.l.], 2006. Disponível em < http://www.codegeneration.net/tiki-index.php?page=FrequentlyAskedQuestions >. Acesso em: 09 mar. 2008.
ECLIPSE. Language IDE. [S.l.], 2008. Disponível em: < http://www.eclipse.org/home/categories/languages.php >. Acesso em: 02 abr. 2008.
FOWLER, M. Patterns of enterprise application architecture. Boston: Addison-Wesley, 2002.
FRANCA, L. P. A., STAA, A. von. A construction process for artifact generators using a CASE tool. In: WORKSHOP ON GENERATIVE PROGRAMMING (GP2002), 7., 2002, Austin. Anais eletrônicos… Austin: Springer-Verlang, 2002, p. 7-9. Disponível em: < http://www.cwi.nl/events/2002/GP2002/GP2002-Proceedings.pdf >. Acesso em: 04 fev. 2008.
FREEMAN, E., et al. Head first design patterns. Sebastopol: O’Reilly, 2004.
GAMMA, E., et al. Design patterns: elements of reusable object-oriented software. Boston: Addison-Wesley, 1995.
126
GARTNER. Gartner says worldwide ralational database market increased 14 percent in 2006. Stamford, 2007. Disponível em: < http://www.gartner.com/it/page.jsp?id=507466 >. Acesso em: 20 jul. 2008.
GREVE, F.G.P., et al. Cordel: uma ferramenta orientada a modelos para a geração de aplicações Web. In: INTERNATIONAL INFORMATION AND TELECOMMUNICATION TECHNOLOGIES SYMPOSIUM, 3., 2004, São Carlos. Anais eletrônicos... São Carlos: [s.n.], 2004. Disponível em: < http://twiki.dcc.ufba.br/pub/Gaudi/Publicacoes/Cordel-SBES2004.pdf >. Acesso em: 02 fev. 2008.
GROSS, C. Foundations of object-oriented programming using .NET 2.0 patterns. New York: Apress, 2006.
HERRINGTON, J. Speed up .NET implementation: how to use code generation for development of database access layers for .NET. [S.l.], 2003a. Disponível em: < http://www.codegeneration.net/downloads/dotNET_db_gen.pdf >. Acesso em: 14 fev. 2008.
HERRINGTON, J. Code generation in action. Greenwich: Manning, 2003b.
HOHPE, G., WOOLF, B. Enterprise integration patterns: designing, building and deploying messaging solutions. Boston: Addison-Wesley, 2003.
IDEABLADE. IdeaBlade DevForce. [S.l.], 2007. Disponível em: < http://www.ideablade.com >. Acesso em: 03 nov. 2007.
ISO. Information technology: programming languages C#. 2.ed. Geneva, 2006. Disponível em: < http://standards.iso.org/ittf/PubliclyAvailableStandards/c042926_ISO_IEC_23270_2006(E).zip >. Acesso em: 11 maio 2008.
KHAN, S., et al. Smart code generator (ASP.Net). [S.l.], 2007. < http://www.codeplex.com/smartcodegenerator >. Acesso em: 03 nov. 2007.
KELLER, W. Mapping objects to tables: a pattern language. In: EUROPEAN CONFERENCE ON PATTERN LANGUAGES OF PROGRAMMING (EuroPLoP ‘97), 2., 1997, Munich. Anais eletrônicos… Munich: Siemens, 1997. Disponível em: < http://www.objectarchitects.de/ObjectArchitects/papers/Published/ZippedPapers/mappings04.pdf >. Acesso em: 16 abr. 2008.
KERIEVSKY, J. Refactoring to patterns. Boston: Addison-Wesley, 2004.
LARMAN, C. Applying UML and patterns : an introdution to object-oriented analysis and design. 3.ed. Boston: Addison-Wesley, 2004.
LAUREANO, P. Introducing TaHoGen: an open source implementation of a CodeSmith-style code generation engine. [S.l.], 2005. Disponível em: < http://www.codeproject.com/tools/TaHoGen.asp >. Acesso em: 03 nov. 2007.
LIQUID. Liquid XML Studio. [S.l.], 2007. Disponível em: < http://www.liquid-technologies.com/Support/XmlStudio/webframe.html >. Acesso em: 04 maio 2008.
MAGELA, R. Engenharia de software aplicada: fundamentos. Rio de Janeiro: Alta Books, 2006.
127
MANGANO, S. XSLT cookbook. 2.ed. Sebastopol: O’Reilly, 2005.
MARINESCU, F. EJB design patterns: advanced patterns, processes and idioms. Indianapolis: Willey Publishing, 2002.
MCALLISTER, N. Oracle still top dog in tough database market: however, open source offerings are nipping at its heels. San Francisco, 2008. Disponível em: < http://computerworld.co.nz/news.nsf/mgmt/66EDF0B2647FB80CCC25747C0000B06C >. Acesso em: 20 jul. 2008.
METSKER, S. J. Design patterns Java workbook. Boston: Addison-Wesley, 2002.
MICROSOFT. Visual Studio developer center. [S.l.], 2008a. Disponível em: < http://msdn2.microsoft.com/pt-br/vstudio/default(en-us).aspx >. Acesso em: 18 jan. 2008.
MICROSOFT. Programming languages. [S.l.] 2008b. Disponível em: < http://msdn2.microsoft.com/en-us/library/aa292164.aspx >. Acesso em: 02 abr. 2008.
MYGENERATION. MyGeneration project. [S.l.], 2007. Disponível em: < http://sourceforge.net/projects/mygeneration/ >. Acesso em: 22 mar. 2008.
MYMETA. MyMeta installer . [S.l.], 2007a. Disponível em: < http://downloads.sourceforge.net/mygeneration/mymeta_installer.exe?modtime=1181957243&big_mirror=0 >. Acesso em: 22 mar. 2008.
MYMETA. MyMeta: MyGeneration software’s meta-data engine. Versão 2.0. [S.l.]: MyGenerator, 2007b. Arquivo Help. Disponível em: < http://downloads.sourceforge.net/mygeneration/mymeta_installer.exe?modtime=1181957243&big_mirror=0 >. Acesso em: 22 mar. 2008.
NIELSEN, P. SQL Server 2005: bible. Indianapolis: Willey Publishing, 2007.
NOCK, C. Data access patterns: database interactions in object-oriented applications. Boston: Addison-Wesley, 2003.
REENSKAUG, T. Thing-Model-View-Editor : an example from a planningsystem. [S.l.], 1979a. Disponível em: < http://heim.ifi.uio.no/~trygver/1979/mvc-1/1979-05-MVC.pdf >. Acesso em: 17 fev. 2008.
REENSKAUG, T. Models-Views-Controllers. [S.l.], 1979b. Disponível em: < http://heim.ifi.uio.no/~trygver/1979/mvc-2/1979-12-MVC.pdf >. Acesso em: 17 fev. 2008.
REENSKAUG, T. The Model-View-Controller (MVC) : its past and present. [S.l.], 2003. Disponível em: < http://heim.ifi.uio.no/~trygver/2003/javazone-jaoo/MVC_pattern.pdf >. Acesso em: 17 fev. 2008.
ROCHA, A.R.C., MALDONADO, J.C., WEBER, K.C. Qualidade de software: teoria e prática. São Paulo: Prentice Hall, 2001.
ROCHA, L.S., et al. XSpeed: uma ferramenta para geração de aplicações distribuídas baseadas em padrões. In: LATIN AMERICAN CONFERENCE ON PATTERN LANGUAGES OF
128
PROGRAMMING, 5., 2005, Campos do Jordão. Anais eletrônicos... Campos do Jordão: USP, 2005. Disponível em: < http://sugarloafplop2005.icmc.usp.br/papers/9695.pdf >. Acesso em: 03 fev. 2008.
RUMBAUGH, J., JACOBSON, I., BOOCH, G. The unified modeling language reference manual. 2.ed. Boston: Addisson-Wesley, 2004.
RUTHERFORD, M. J., WOLF, A. L. A case for test-code generation in model-driven systems. In: INTERNATION CONFERENCE ON GENERATIVE PROGRAMMING AND COMPONENT ENGENEERING (GPCE’2003), 2., 2003, Enfurt. Anais eletrônicos… Enfurt: Spring-Verlang, 2003, v. 48. p. 377-396. Disponível em: < http://portal.acm.org/ft_gateway.cfm?id=954209&type=pdf&coll=portal&dl=ACM&CFID=19454306&CFTOKEN=36545870 >. Acesso em: 09 mar. 2008.
SEARA, E. F. R. Incrementando o desenvolvimento de sistemas baseados em framework através de ferramenta RAD. 2005. 108 f. Trabalho de Conclusão de Curso (Graduação em Ciência da Computação) – Centro de Ciências Tecnológicas da Terra e do Mar, Universidade do Vale do Itajaí, Itajaí, 2005.
SELLS, C. Generative programming: modern techniques to automate repetitive programming tasks. MSDN Magazine, [S.l.], dec. 2001. Disponível em: < http://msdn2.microsoft.com/en-us/magazine/cc301675.aspx >. Acesso em: 30 mar. 2008.
SHALLOWAY, A; TROTT, J.R. Design patterns explained: a new perspective on object-oriented design. 2. ed. Boston: Addison-Wesley, 2004.
SILVA, A. C. Ferramenta CASE para automatizar a criação de testes de unidade. 2007. 121 f. Trabalho de Conclusão de Curso (Graduação em Ciência da Computação) – Centro de Ciências Tecnológicas da Terra e do Mar, Universidade do Vale do Itajaí, Itajaí, 2007.
SILVEIRA, C. Geração automática de cadastros e consultas para linguagem ASP baseado em banco de dados. 2003. 77 f. Trabalho de Conclusão de Curso (Graduação em Ciências da Computação) – Centro de Ciências Exatas e Naturais, Universidade Regional de Blumenau, Blumenau, 2003.
SINGH, I., et al. Designing enterprise applications with J2EE platform. 2.ed. Boston: Addison-Wesley, 2002.
SMARTCODEGENERATOR. SmartCodeGenerator quick start v2.0. [S.l.], 2006. Disponível em: < http://www.smartcodegenerator.com/ >. Acesso em: 30 mar. 2008.
SOLUTIONSDESIGN. LLBLGen Pro : the #1 n-tier generator and O/R mapper. [S.l.], 2007. Disponível em: < http://www.llblgen.com >. Acesso em: 03 nov. 2007.
SOMMERVILLE, I. Engenharia de software. 8.ed. São Paulo: Prentice Hall, 2007.
SOSNOSKI, D. Data binding, part 1: code generation approaches – JAXB and more. [S.l.], 2003. Disponível em: < http://www.ibm.com/developerworks/library/x-databdopt/ >. Acesso em: 09 mar. 2008.
129
SOURCEFORGE. NVelocity. [S.l.], 2003. Disponível em: < http://nvelocity.sourceforge.net/ >. Acesso em: 21 jul. 2008.
STELTING, S., MAASSEN, O. Applied Java patterns. New Jersey: Prentice Hall, 2001.
SUNMICROSYSTEMS. Java blue prints: model-view-controller. [S.l.], 2002a. Disponível em: < http://java.sun.com/blueprints/patterns/MVC-detailed.html >. Acesso em: 23 out. 2007.
SUNMICROSYSTEMS. Core J2EE patterns: data access object. [S.l.], 2002b. Disponível em: < http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html >. Acesso em: 25 out. 2007.
TROWBRIDGE, D. et at. Enterprise solution patterns using Microsoft .NET. 2.ed. [S.l.]: Microsoft, 2003. Disponível em: <http://www.microsoft.com/downloads/details.aspx?FamilyId=3C81C38E-ABFC-484F-A076-CF99B3485754&displaylang=en >. Acesso em: 02 mar. 2008.
TROWBRIDGE, D. et at. Microsoft integration patterns. [S.l.]: Microsoft, 2004. Disponível em: < http://www.microsoft.com/downloads/details.aspx?FamilyId=3E58C8D3-CE19-4101-8048-7CF6EABA0929&displaylang=en >. Acesso em: 02 mar. 2008.
VLIST, E. van der. XML schema. Sebastopol: O’Reilly, 2002.
VOLANAKIS, E. Mixed-language usage scenarios in Eclipse. In: ECLIPSE LANGUAGES SYMPOSIUM, 1., 2005, Ottawa. Anais eletrônicos... Ottawa: Eclipse, 2005. Disponível em: < http://www.eclipse.org/org/langsymp/Volanakis%20-%20Mixed-language%20usage%20scenarios.pdf >. Acesso em: 02 abr. 2008.
W3C. Extensible markup language (XML) 1.0: W3C Recommendation. [S.l.]: W3C, 1998. Disponível em: < http://www.w3.org/TR/1998/REC-xml-19980210 >. Acesso em: 04 maio 2008.
WOLFF, E., SCHMIDT, A., VÖLTER, M. Building EJB applications: a collection of patterns. In: CONFERENCE ON PATTERN LANGUAGES OF PROGRAMS (PLoP’2001), 8., 2001, Monticello. Anais eletrônicos… Monticello: [s.n.], 2001. Disponível em: < http://jerry.cs.uiuc.edu/~plop/plop2001/accepted_submissions/PLoP2001/mwolff0/PLoP2001_mwolff0_1.pdf >. Acesso em: 02 mar. 2008.
APÊNDICES
A CENÁRIOS E PROTÓTIPOS DE TELA DOS CASOS DE USO
A.1.1.1 UC01 – Cria projeto
Caso de uso responsável por criar um novo projeto habilitando as funcionalidades da
ferramenta.
Relações
• RF11.
Condições
• Pós Condição: Um projeto foi criado.
Cenários
Cria projeto {Principal}
1. Usuário informa os dados do projeto a ser criado (Figura 63).
2. Sistema verifica autenticidade dos dados.
3. Sistema cria projeto.
Preenchimento de campos obrigatórios {Exceção}
Se no passo 1 o sistema verificar que alguma informação obrigatória não foi fornecida:
1.1. Sistema apresenta mensagem: “Por favor,.preencha o campo com alguma informação.”.
1.2. Retorna ao passo 1.
Diretório inexistente (Exceção)
Se no passo 2 o diretório informado não existir:
2.1. Sistema apresenta mensagem: “O diretório informado não existe.”.
2.2. Aborta criação do projeto.
2.3. Retorna para o passo 1.
Arquivo existente (Alternativo)
Se no passo 2 o arquivo informado já existir:
2.1. Sistema sobreescreve o arquivo existente.
132
A.1.1.2 UC02 – Abre projeto
Caso de uso responsável por abrir um projeto existente, permitindo ao usuário continuar ou
alterar a configuração das classes a serem geradas.
Relações
• RF12.
Condições
• Pré Condição: Algum projeto já deve ter sido criado e salvo em um arquivo no formato
“KPRJ” (Keops Project).
• Pós Condição: Um projeto foi aberto.
Cenários
Abre projeto {Principal}
1. Usuário informa o arquivo de projeto que deve ser aberto (Figura 79).
2. Sistema verifica a validade do arquivo.
3. Sistema lê as informações do arquivo e cria a estrutura de classes com suas respectivas
configurações.
4. Sistema abre projeto.
Arquivo não encontrado {Exceção}
Se no passo 2 o sistema verificar que o diretório ou arquivo não existe:
2.1. Sistema apresenta mensagem informando que não foi possível localizar o diretório ou o
arquivo especificado.
2.2. Retorna ao passo 1.
Erro ao abrir arquivo {Exceção}
Se no passo 2 ocorrer algum erro:
2.1. Sistema apresenta mensagem: “Não foi possível ler o arquivo <nome_do_arquivo>. O
arquivo pode estar corrompido ou não ser um arquivo XML válido. Erro ocorrido:
<mensagem_do_erro>”.
2.2. Sistema aborta execução.
133
Figura 79. Interface para abrir um projeto existente
A.1.1.3 UC03 – Configura mapeamento de tipos
Caso de uso que permite ao usuário configurar o mapeamento entre os tipos de dados dos
bancos de dados suportados e os tipos de dados suportados pela linguagem de programação
selecionada, facilitando a posterior configuração dos atributos de classes a serem gerados pelo
sistema.
Relações
• RF10.
Condições
• Pós Condição: Mapeamento entre os tipos de dados dos bancos de dados suportados e os
tipos suportados pela linguagem de programação selecionada pelo usuário.
Cenários
Configura mapeamento entre tipos de dados do banco de dados e da linguagem de
programação {Principal}
1. Usuário solicita ao sistema a possibilidade de definir o mapeamento entre os tipos de
dados dos bancos de dados e os tipos de dados da linguagem desejada.
134
2. Sistema apresenta tela de configuração ao usuário (Figura 64).
3. Usuário seleciona o tipo de banco de dados desejado.
4. Usuário seleciona a linguagem de programação desejada.
5. Usuário informa o mapeamento entre tipos de dados do banco de dados e da linguagem de
programação.
6. Usuário confirma operação.
7. Sistema valida os dados informados.
8. Sistema salva as configurações realizadas.
Preenchimento de campos obrigatórios {Exceção}
Se no passo 7 o sistema verificar que alguma informação obrigatória não foi fornecida:
7.1. Sistema apresenta mensagem: “Por favor,.preencha o campo com alguma informação.”.
7.2. Retorna ao passo 3.
A.1.1.4 UC04 – Configura acesso ao banco de dados
Caso de uso que permite configurar as informações utilizadas pela aplicação para acessar o
banco de dados, listando todas as tabelas do banco de dados ou schema disponíveis para geração.
Relações
• RF01 e RF02.
Condições
• Pré Condição: Ter um projeto aberto ou criado; e
• Pós Condição: Listagem das tabelas do banco de dados ou schema disponíveis para
geração de código.
Cenários
Configura informações de acesso {Principal}
1. Usuário fornece as informações de acesso ao banco de dados (Figura 67).
2. Sistema valida informações.
3. Sistema estabelece conexão com o banco de dados.
4. O sistema apresenta ao usuário as tabelas existentes no banco de dados ou schema
selecionado durante a configuração de acesso (Figura 68 – Painel Tabelas disponíveis).
135
Preenchimento de campos obrigatórios {Exceção}
Se no passo 2 o sistema verificar que alguma informação obrigatória não foi fornecida:
2.1. Sistema apresenta a mensagem: “Por favor,.preencha o campo com alguma
informação.”.
2.2. Retorna ao passo 1.
Erro ao estabelecer conexão com o banco de dados {Exceção}
Se no passo 3 ocorrer algum erro de conexão:
3.1. O sistema apresenta a mensagem: “Não foi possível estabelecer conexão com o banco de
dados. Verifique as informações fornecidas e certifique-se que o usuário e senha informados
são válidos. Erro ocorrido: <mensagem_de_erro>.”.
3.2. O sistema aborta a configuração e acesso ao banco de dados.
Cancela configuração e acesso ao banco de dados {Alternativo}
Se durante o passo 1 o usuário decide cancelar as configurações e acesso ao banco de dados:
1.1. Usuário informa ao sistema que deve cancelar as configurações e acesso ao banco de
dados (Figura 67 – Botão “Cancelar”).
1.2. O sistema aborta a configuração e acesso ao banco de dados.
A.1.1.5 UC05 - Configura classes a serem geradas
Caso de uso responsável por fornecer ao usuário a possibilidade de selecionar quais as
classes que devem ser geradas, bem como configurar as propriedades, atributos e métodos das
classes a serem geradas.
Relações
• RF03, RF05, RF06, RF07, RF08, RF09, RF10, RF18, RN01, RN03, RN04, RN05,
RN09, RN10, RN11 e RN12.
Condições
• Pré Condição: Ter disponível a lista de tabelas do banco de dados que estão disponíveis
para geração de código.
• Pós Condição: Configuração das classes a serem geradas.
136
Cenários
Configura as propriedades das classes a serem geradas {Principal}
1. O usuário define quais tabelas devem ser geradas.
2. O usuário configura as propriedades das classes mapeadas a serem geradas (Figura 71).
3. O sistema valida as informações fornecidas.
Exclui classe {Alternativo}
Se no passo 1 o usuário decidir excluir alguma das classes selecionadas para geração:
1.1. O usuário clica com o botão direito do mouse sobre a tabela desejada e seleciona a
opção “Excluir”.
1.2. Sistema apresenta a mensagem “Deseja realmente excluir a classe <nome_da_classe> do
projeto?”.
1.3. O usuário confirma a operação.
1.4. O sistema exclui a classe do projeto.
Configura atributos de classe {Alternativo}
Se no passo 2 o usuário decidir configurar quais os atributos de classe devem ser gerados:
2.1. O usuário seleciona quais os atributos devem ser gerados (Figura 71 – Aba Atributos).
2.1. O usuário configura as propriedades dos atributos de classe a serem gerados.
2.2. O sistema valida as informações fornecidas.
Configura métodos de classe {Alternativo}
Se no passo 2 o usuário decidir configurar quais os métodos de classe devem ser gerados:
2.1. Usuário solicita a criação de um novo método (Figura 71 – Aba Métodos – Botão
Adicionar).
2.2. Usuário seleciona o tipo de método a ser gerado (Figura 72 – Campo Tipo).
2.3. Sistema apresenta o comando SQL padrão para o tipo de método selecionado.
2.4. Usuário configura as demais propriedades do método.
2.3. O sistema valida as informações fornecidas.
Altera comando SQL do método {Alternativo}
Se no passo 2.4 do cenário “Configura métodos de classe” o usuário decidir alterar o
comando SQL gerado automaticamente pela ferramenta:
137
2.4.1. Usuário informa o comando SQL desejado (Figura 72 – Campo Comando SQL).
2.4.2. Usuário solicita validação do comando SQL informado (Figura 72 – Botão Validar).
2.4.3. Sistema valida comando SQL fornecido pelo usuário.
Decompõe tabela em várias classes {Alternativo}
Se no passo 1 o usuário decidir decompor uma ou mais tabelas em mais de uma classe:
1.1. O usuário clica com o botão direito do mouse sobre a tabela desejada e seleciona a
opção “Decompor tabela”.
1.2. O sistema apresenta a tela para configuração (Figura 80).
1.3. O usuário configura quais as classes que devem ser criadas a partir da decomposição da
tabela selecionada.
1.4. O usuário configura quais os atributos da tabela que serão mapeados para cada uma das
classes.
1.5. O usuário confirma a operação.
1.6. O sistema cria as classes com base nas configurações realizadas pelo usuário.
Cria generalização a partir de duas tabelas {Alternativo}
Se no passo 1 o usuário decidir criar uma classe base que compõe os atributos comuns de
duas ou mais tabelas do banco de dados:
1.1. O usuário seleciona as tabelas desejadas.
1.2. O usuário clica com o botão direito do mouse sobre uma das tabelas selecionadas e
escolhe a opção “Criar classe base”.
1.3. O sistema apresenta a tela para configuração (Figura 69).
1.4. O usuário configura quais atributos de cada uma das classes deve ser membro da classe
base.
1.5. O usuário confirma a operação.
1.6. O sistema cria a classe base baseado nas configurações realizadas pelo usuário.
Define generalização entre classes existentes {Alternativo}
Se no passo 2 o usuário verificar que a classe que está sendo configurada é uma
generalização de uma classe base, e portanto, necessita definir a estrutura de herança entre as
duas classes:
138
2.1. O usuário seleciona qual classe deve ser considerada a classe base para a classe em
questão (Figura 71 – Campo Classe base).
2.2. O sistema configura a classe em questão como sendo uma subclasse da classe base
selecionada pelo usuário.
Preenchimento de campos obrigatórios {Exceção}
Se no passo 3 o sistema verificar que o usuário não forneceu alguma informação obrigatória:
3.1. O sistema apresenta a mensagem: “Por favor, preencha o campo com alguma
informação.”.
3.2. Retorna ao passo 2.
Nome de classe já utilizado {Exceção}
Se no passo 3 o sistema verificar que o nome informado para a classe já é utilizado por outra
classe:
3.1. O sistema apresenta a mensagem: “Já existe uma classe com o mesmo nome. Por favor,
selecione outro nome para a classe.”.
3.2. Retorna ao passo 2.
Sem conexão com o banco de dados {Exceção}
Se no passo 2.4.3 o sistema verificar que não há conexão com o banco de dados:
2.4.3.1. Sistema apresenta a mensagem: “Não foi possível estabelecer conexão com o banco
de dados. Verifique as informações fornecidas e certifique-se que o usuário e senha
informados são válidos. Erro ocorrido: <mensagem_de_erro>.”.
2.4.3.2. Aborta a operação.
Atributo de classe não existente na tabela do banco de dados {Exceção}
Se no passo 2.4.3 o sistema verificar que algum dos atributos da classe não possui uma
coluna correspondente na tabela mapeada:
2.4.3.1. Sistema apresenta a mensagem: “A classe selecionada possui atributos que
referenciam colunas inexistentes na tabela do banco de dados. Caso tais colunas tenham sido
utilizadas no comando SQL, será gerado um erro informando tal inconsistência. Deseja
cancelar a validação do comando SQL?”.
139
2.4.3.2. O usuário confirma o cancelamento da validação do comando SQL clicando no
botão “Sim”.
2.4.3.3. Sistema aborta a operação.
Valida comando SQL com diferença nas estruturas de classe e tabela {Alternativo}
Se no passo 2.4.3.2 o usuário decidir ignorar a diferença entre a estrutura de atributos da
classe e a estrutura de colunas da tabela e continuar com a validação do comando SQL:
2.4.3.2.1. Usuário ignora o cancelamento de validação do comando SQL clicando no botão
“Não”.
2.4.3.2.2. Sistema valida o comando SQL informado pelo usuário.
Comando SQL inválido {Exceção}
Se no passo 2.4.3 ou no passo 2.4.3.2.2 o sistema verificar que o comando SQL informado é
inválido:
2.4.3.1. Sistema apresenta a mensagem: “Comando SQL inválido. Detalhes do erro:
<mensagem_de_erro>.”.
2.4.3.2. Retorna ao passo 2.4.3.
Figura 80. Interface para configurar as classes que devem ser criadas a partir da decomposição de uma tabela
140
A.1.1.6 UC06 - Parametriza arquivos templates
Caso de uso que permite ao usuário definir os arquivos templates a serem utilizados durante
o processo de geração de código-fonte, sendo possível definir arquivos a nível de projeto e a nível
de classe;
Relações
• RF15, RF16, RN06, RNF06, RN07 e RN08.
Condições
• Pré Condição: Ter um projeto aberto ou criado.
• Pré Condição: Algum arquivo template já deve ter sido criado no formato “KTPL”.
• Pós Condição: Definição dos arquivos templates a serem utilizados durante a geração de
código.
Cenários
Define arquivo template para o projeto {Principal}
1. Usuário solicita ao sistema a opção de definir um arquivo template para o projeto.
2. O sistema apresenta a tela de seleção de arquivos ao usuário (Figura 73).
3. O usuário seleciona a opção de adicionar arquivo (Figura 73 – Botão Adicionar).
4. O usuário seleciona o arquivo desejado.
5. O sistema valida o arquivo selecionado.
6. O sistema associa o arquivo template ao projeto.
Define arquivo template para uma classe {Alternativo}
Se no passo 1 o usuário perceber a necessidade de utilizar um arquivo ou conjunto de
arquivos templates específicos para uma determinada classe:
1.1. Usuário clica com o botão direito do mouse sobre a classe desejada e seleciona a opção
“Gerenciador de templates”.
1.2. O sistema apresenta a tela de seleção de arquivos ao usuário (Figura 73).
1.3. O usuário seleciona a opção de adicionar arquivo (Figura 73 – Botão Adicionar).
1.4. O usuário seleciona o arquivo desejado.
1.5. O sistema valida o arquivo selecionado.
1.6. O sistema associa o arquivo template a classe.
141
Remover arquivo template {Alternativo}
Se no passo 3 o usuário solicitar ao sistema a remoção de um arquivo da lista de arquivos
templates utilizados para a geração de código:
3.1. O usuário solicita a remoção do arquivo template da lista de arquivos (Figura 73 – Botão
Remover).
3.2. O sistema mostra a mensagem: “Deseja realmente remover o(s) arquivo(s)
selecionado(s)?”.
3.3. O usuário confirma.
3.4. O sistema remove o arquivo template selecionado.
Definir sufixo a ser utilizado nos arquivos gerados com base no template {Alternativo}
Se no passo 3 o usuário perceber a necessidade de associar um sufixo ao arquivo template:
3.1. O usuário clica com o botão direito do mouse sobre o(s) arquivo(s) desejado(s) e
seleciona a opção “Editar sufixo”.
3.2. Sistema apresenta tela ao usuário
3.3. Usuário preenche as informações necessárias.
3.4. Sistema valida o preenchimento dos dados.
3.5. Sistema associa o sufixo ao(s) arquivo(s) template(s) selecionado(s).
Definir extensão de arquivo a ser utilizada nos arquivos gerados com base no template
{Alternativo}
Se no passo 3 o usuário perceber a necessidade de associar uma extensão de arquivo
específica ao arquivo template:
3.1. O usuário clica com o botão direito do mouse sobre o(s) arquivo(s) desejado(s) e
seleciona a opção “Editar extensão de arquivo”.
3.2. Sistema apresenta tela ao usuário
3.3. Usuário preenche as informações necessárias.
3.4. Sistema valida o preenchimento dos dados.
3.5. Sistema associa a extensão de arquivo ao(s) arquivo(s) template(s) selecionado(s).
Diretório ou arquivo não encontrado {Exceção}
Se nos passos 5 ou 1.5 o sistema verificar que o diretório ou arquivo não existe:
142
5.1. Sistema apresenta a mensagem informando que não foi possível localizar o diretório ou
arquivo.”.
5.2. Retorna ao passo 2.
Preenchimento de campos obrigatórios {Exceção}
Se no passo 3.4 o sistema verificar que o usuário não forneceu alguma informação
obrigatória:
3.4.1. O sistema apresenta a mensagem: “Por favor, preencha o campo com alguma
informação.”.
3.2. Retorna ao passo 3.3.
A.1.1.7 UC07 – Verifica estrutura de atributos
Caso de uso que fornece ao usuário a possibilidade de comparar a estrutura de atributos de
uma classe com os campos da tabela do banco de dados relacionada a classe, permitindo validar se a
estrutura de atributos da classe é compatível com a estrutura de campos da tabela mapeada.
Relações
• RF17 e RN13.
Condições
• Pré Condição: Ter um projeto aberto.
• Pré Condição: Ter realizado o mapeamento entre, no mínimo, uma tabela e uma classe.
• Pós Condição: Comparação entre estrutura de atributos de classe e estrutura de campos
da tabela realizada.
Cenários
Valida estrutura {Principal}
1. Usuário seleciona a tabela desejada.
2. Usuário clica com o botão direito do mouse e seleciona a opção “Validar estrutura das
tabelas mapeadas”.
3. Sistema busca a estrutura da(s) tabela(s) relacionada(s) à classe no banco de dados.
143
4. Sistema compara a estrutura de atributos da classe com a estrutura de campos da(s)
tabela(s).
5. Sistema apresenta resultado da validação realizada.
6. Usuário confirma a operação.
Exclui atributos de classe sem coluna relacionada {Alternativo}
Se no passo 6 o usuário decidir excluir atributos de classe que não referenciam um campo
existente na tabela do banco de dados:
6.1. Usuário seleciona os atributos que deseja excluir.
6.2. Usuário solicita a exclusão dos atributos que não possuem um campo relacionado na
tabela do banco de dados (Figura 81 – Botão Remover).
6.3. Sistema exclui os atributos da classe selecionados.
Adiciona colunas não mapeadas {Alternativo}
Se no passo 6 o usuário decidir adicionar colunas da tabela que ainda não foram mapeados
na classe:
6.1. Usuário seleciona colunas a serem incluídas.
6.2. Usuário solicita o mapeamento das colunas para atributos da classe (Figura 81 – Botão
Adicionar).
6.3. Sistema adiciona as colunas selecionadas à estrutura de atributos da classe.
Sem conexão com o banco de dados {Exceção}
Se no passo 3 não houver uma conexão estabelecida com o banco de dados:
1. Sistema apresenta mensagem: “Não foi possível estabelecer conexão com o banco de
dados. Verifique as informações fornecidas e certifique-se que o usuário e senha informados
são válidos. Erro ocorrido: <mensagem_de_erro>.”.
2. Aborta a operação.
144
Figura 81. Interface que valida estrutura de atributos de classe com estrutura de colunas da tabela
A.1.1.8 UC08 - Salva projeto
Caso de uso responsável por fornecer ao usuário a opção de salvar o projeto para posterior
alteração nas configurações.
Relações
• RF13 e RNF05.
Condições
• Pré Condição: Ter um projeto aberto ou criado.
• Pós Condição: Projeto salvo em formato XML de acordo com o XML Schema definido.
Cenários
Salva projeto {Principal}
145
1. Usuário solicita ao sistema salvar o arquivo.
2. O sistema salva o arquivo com o nome e diretório definidos na criação do projeto.
3. O sistema mostra a mensagem: “O arquivo <nome_do_arquivo> foi salvo com sucesso.”.
Erro ao salvar arquivo {Exceção}
Se durante o passo 2 ocorrer algum erro:
2.1. O sistema mostra a mensagem: “Não foi possível salvar o arquivo <nome_do_arquivo>.
Erro ocorrido: <mensagem_de_erro>.”.
2.2. O sistema aborta a operação.
A.1.1.9 UC09 - Gera código-fonte
Caso de uso responsável por gerar o código-fonte baseado nas configurações realizadas pelo
usuário e nos arquivos templates selecionados para geração. Os arquivos templates utilizados no
processo de geração são definidos a nível de projeto e a nível de classe, onde o arquivo template a
nível de projeto é ignorado caso haja um arquivo template associado à classe a ser gerada.
Relações
• RF14, RNF07, RN06, RN07, RN08.
Condições
• Pré Condição: Ter um projeto aberto ou criado, selecionado as tabelas a serem geradas e
parametrizado o(s) arquivo(s) template(s) .
• Pós Condição: Arquivo(s) de código-fonte gerado(s) com base nas configurações
realizadas pelo usuário e no(s) arquivo(s) template(s) configurado(s).
Cenários
Gera código-fonte {Principal}
1. O usuário solicita ao sistema a geração de código-fonte (Figura 74 – Botão Gerar).
2. O sistema gera os arquivos de código-fonte, substituindo os arquivos já existentes, caso
haja algum.
Salvar arquivo de log {Alternativo}
Se no passo 2 o usuário decidir salvar o log dos arquivo gerados:
146
2.1. O usuário solicita o armazenamento do arquivo de log (Figura 74 – Botão Salvar log).
2.2. Usuário seleciona o diretório e nome do arquivo de log a ser salvo.
2.3. O sistema salva o arquivo de log.
2.4. O sistema mostra a mensagem “O arquivo <nome_do_arquivo> foi salvo com sucesso.
Erro ao salvar arquivo de log {Exceção}
Se no passo 2.3 do cenário “Salvar arquivo de log” ocorrer algum erro:
2.3.1. O sistema mostra a mensagem “Não foi possível salvar o arquivo de log
<nome_do_arquivo>. Erro ocorrido: <mensagem_de_erro>.”
2.3.2. O sistema aborta a operação.
Arquivo template não encontrado{Exceção}
Se no passo 2 o sistema verificar que algum arquivo template configurado não existe:
2.1. O sistema mostra a mensagem: “Erro ocorrido durante a geração da classe
<nome_da_classe>. Não foi encontrado o arquivo template <nome_do_arquivo>.”.
2.2. O sistema aborta a geração do código-fonte para o template em questão.
2.3. Caso haja mais arquivos templates ou classes a serem geradas, o sistema seleciona o
próximo arquivo template a ser utilizado ou próxima classe a ser gerada e retorna ao passo 2.
Sintaxe do arquivo template inválida {Exceção}
Se no passo 2 o sistema verificar que algum arquivo template configurado não existe:
2.1. O sistema mostra a mensagem: “Erro ocorrido durante a geração da classe
<nome_da_classe>. Não foi encontrado o arquivo template <nome_do_arquivo>.”.
2.2. O sistema aborta a geração do código-fonte para o template em questão.
2.3. Caso haja mais arquivos templates ou classes a serem geradas, o sistema seleciona o
próximo arquivo template a ser utilizado ou a próxima classe a ser gerada e retorna ao passo
2.
A.1.1.10 UC10 – Configura opções do projeto
Caso de uso responsável por configurar as opções do projeto, como por exemplo, os
mapeamentos entre tipos a serem utilizados e opções de geração de código-fonte.
147
Relações
• RF14, RN06.
Condições
• Pré Condição: Ter um projeto aberto ou criado e ter definido a lista de mapeamentos
entre tipos do banco de dados e tipos da linguagem de programação.
• Pós Condição: Configurações de mapeamentos de tipos e opções de geração de código-
fonte definidas.
Cenários
Configura mapeamento de tipos a ser utilizado {Principal}
1. O usuário solicita a tela de configuração ao sistema.
2. O sistema apresenta a tela ao usuário.
3. O usuário configura o mapeamento desejado (Figura 65).
4. O usuário confirma as configurações.
5. Sistema define os mapeamentos configurados pelo usuário como padrão.
Configura opções de geração de código-fonte {Alternativo}
Se no passo 3 o usuário decidir configurar as opções de geração de código-fonte:
3.1. O usuário configura as opções de geração de código-fonte desejadas (Figura 66).
3.2. O usuário confirma as configurações.
3.3. Sistema define as opções de geração de código-fonte.
148
B CÓDIGO-FONTE DO XML SCHEMA
<?xml version="1.0" encoding="utf-8" ?> <!-- Created with Liquid XML Studio 1.0.8.0 (http:/ /www.liquid-technologies.com) --> <xs:schema id="ProjectSchema" xmlns:xs="http://www. w3.org/2001/XMLSchema"> <xs:element name="Project"> <xs:complexType> <xs:sequence> <xs:element name="DefautLanguageMappingInde x" type="xs:integer" /> <xs:element name="DefaultProviderMappingInd ex" type="xs:integer" /> <xs:element name="LanguageMappingFileName" type="xs:string" /> <xs:element name="ProviderMappingFileName" type="xs:string" /> <xs:element name="Name" type="xs:string" /> <xs:element name="Database"> <xs:complexType> <xs:sequence> <xs:element name="ConnectionSettings" type="ConnectionSettings" /> <xs:element name="Tables"> <xs:complexType> <xs:sequence> <xs:element minOccurs="0" maxOc curs="unbounded" name="Table"> <xs:complexType> <xs:sequence> <xs:element name="Name" t ype="xs:string" /> <xs:element name="Descrip tion" type="xs:string" /> <xs:sequence> <xs:element name="Field s"> <xs:complexType> <xs:sequence> <xs:element minOc curs="0" maxOccurs="unbounded" name="Field"> <xs:complexType > <xs:complexCo ntent mixed="false"> <xs:extensi on base="Field" /> </xs:complexC ontent> </xs:complexTyp e> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="Name" type="xs:stri ng" /> <xs:attribute name="Owner" type="xs:str ing" /> </xs:complexType> </xs:element> <xs:element name="Classes"> <xs:complexType> <xs:sequence> <xs:element minOccurs="0" maxOccurs=" unbounded" name="Class"> <xs:complexType> <xs:sequence> <xs:element name="Name" type="x s:string" /> <xs:element minOccurs="0" name= "BaseClass" type="xs:string" /> <xs:element name="Tables"> <xs:complexType>
149
<xs:sequence> <xs:element maxOccurs="un bounded" name="Table" type="xs:string" /> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="Attributes"> <xs:complexType> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" name="Attribute" type="Attribute" /> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="Methods"> <xs:complexType> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" name="Method"> <xs:complexType> <xs:sequence> <xs:element name="N ame" type="xs:string" /> <xs:element name="R eturnType" type="xs:string" /> <xs:element name="S QLCommand" type="xs:string" /> <xs:element name="P arameters"> <xs:complexType> <xs:sequence> <xs:element m inOccurs="0" maxOccurs="unbounded" name="Parameter" type="xs:str ing" /> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="T ype" type="xs:integer" /> <xs:attribute name="S cope" type="xs:string" use="required" /> <xs:attribute name="O verride" type="xs:boolean" use="required" /> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="IsAbstract" t ype="xs:boolean" use="required" /> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="Templates"> <xs:complexType> <xs:sequence> <xs:element minOccurs="0" maxOccurs=" unbounded" name="Template"> <xs:complexType> <xs:sequence> <xs:element name="Owner" type=" xs:string" /> <xs:element name="Suffix" type= "xs:string" /> <xs:element name="Filename" typ e="xs:string" /> <xs:element name="FileExtension " type="xs:string" /> </xs:sequence> <xs:attribute name="Scope" type=" xs:integer" /> </xs:complexType> </xs:element> </xs:sequence>
150
</xs:complexType> </xs:element> <xs:element name="Generator"> <xs:complexType> <xs:sequence> <xs:element name="TargetDirectory" ty pe="xs:string" /> <xs:element name="DefaultExtension" t ype="xs:string" /> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="TargetLanguage" type="xs: string" /> </xs:complexType> </xs:element> <xs:complexType name="ConnectionSettings"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="1" name= "DatabaseName" type="xs:string" /> <xs:element minOccurs="1" maxOccurs="1" name= "DataSource" type="xs:string" /> <xs:element minOccurs="1" maxOccurs="1" name= "DBType" type="xs:integer" /> <xs:element minOccurs="1" maxOccurs="1" name= "Password" type="xs:string" /> <xs:element minOccurs="0" maxOccurs="1" name= "PortNumber" type="xs:integer" /> <xs:element minOccurs="0" maxOccurs="1" name= "Provider" type="xs:string" /> <xs:element minOccurs="1" maxOccurs="1" name= "Username" type="xs:string" /> </xs:sequence> </xs:complexType> <xs:complexType name="Field"> <xs:sequence> <xs:element name="DefaultValue" type="xs:stri ng" /> <xs:element name="Description" type="xs:strin g" /> <xs:element minOccurs="1" maxOccurs="1" name= "Name" type="xs:string" /> <xs:element minOccurs="1" maxOccurs="1" name= "Type" type="xs:string" /> <xs:element minOccurs="1" maxOccurs="1" name= "IsAutoKey" type="xs:boolean" /> <xs:element minOccurs="1" maxOccurs="1" name= "IsForeignKey" type="xs:boolean" /> <xs:element minOccurs="1" maxOccurs="1" name= "IsNullable" type="xs:boolean" /> <xs:element minOccurs="1" maxOccurs="1" name= "IsPrimaryKey" type="xs:boolean" /> <xs:element name="MaxLenght" type="xs:integer " /> <xs:element name="NumericPrecision" type="xs: integer" /> <xs:element name="NumericScale" type="xs:inte ger" /> </xs:sequence> </xs:complexType> <xs:complexType name="Attribute"> <xs:sequence> <xs:element name="DefaultValue" type="xs:stri ng" /> <xs:element name="Description" type="xs:strin g" /> <xs:element name="IsGenerated" type="xs:boole an" /> <xs:element name="IsReadOnly" type="xs:boolea n" /> <xs:element name="IsStatic" type="xs:boolean" /> <xs:element name="Name" type="xs:string" /> <xs:element name="PropertyName" type="xs:stri ng" /> <xs:element name="ProviderDataType" type="xs: string" /> <xs:element name="Scope" type="xs:string" /> <xs:element name="Type" type="xs:string" /> <xs:element name="UnderlyingDBObjects"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="unbounded" name= "UnderlyingObject"> <xs:complexType> <xs:sequence>
151
<xs:element name="TableName" type ="xs:string" /> <xs:element name="FieldName" type ="xs:string" /> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:schema>
152
C TEMPLATES C#
C.1 DTO TEMPLATE using System; #set ($header = "public") #if ($currentClass.IsAbstract) #set ($header = "$header abstract") #end #set ($header = "$header class $currentClass.Name") #if ($currentClass.BaseClass.Name != "") #set ($header = "$header: $currentClass.BaseClass.N ame") #end $header { #region Attributes #**##foreach($attribute in $currentClass.Fields.Val ues) #**##if ($attribute.IsGenerated) #**# $attribute.Scope $attribute.Type $attribute .Name; #**##end #**##end #endregion #region Properties #foreach($attribute in $currentClass.Fields.Val ues) #if ($attribute.IsGenerated) $attribute.Scope $attribute.Type $attribute.Pro pertyName { get { return $attribute.Name; } set { $attribute.Name = value; } } #end #end #endregion }
C.2 DAO TEMPLATE using System; using System.Data; using Npgsql; #set ($header = "public") #if ($currentClass.IsAbstract) #set ($header = "$header abstract") #end #set ($header = "$header class ${currentClass.Name} DAO") #if ($currentClass.BaseClass.Name != "") #set ($header = "$header: ${currentClass.BaseClass. Name}DAO") #end $header { #**##if ($currentClass.IsAbstract) #**# protected const string STR_CONN = @"$currentProject.Database.ConnectionSettings.Conne ctionString"; #**##elseif ($currentClass.BaseClass.Name != "") #**##else
153
#**# private const string STR_CONN = @"$currentProject.Database.ConnectionSettings.Conne ctionString"; #**##end #foreach($method in $currentClass.Methods) #if ($method.TypeAsString == "Insert" || $method.Ty peAsString == "Update" || $method.TypeAsString == "Delete") #**##set ($SqlCommand = $method.SqlCommand) #**##if ($currentClass.IsAbstract) #* *##set ($methodHeader = "$method.Scope abst ract $method.ReturnType ${method.Name}(${currentClass.Name} p)") #**##elseif ($method.Override) #* *##set ($methodHeader = "$method.Scope over ride $method.ReturnType ${method.Name}(${currentClass.BaseClass.Name} p)") #**##else #* *##set ($methodHeader = "$method.Scope $met hod.ReturnType ${method.Name}(${currentClass.Name} p)") #**##end #**##if ($currentClass.IsAbstract) #**# ${methodHeader}; #**##else #**# ${methodHeader} { string sqlCommand = @"${SqlCommand}"; NpgsqlConnection conn = new NpgsqlConnectio n(STR_CONN); conn.Open(); NpgsqlTransaction trans = conn.BeginTransac tion(); NpgsqlCommand cmd = new NpgsqlCommand(sqlCo mmand, conn, trans); try { #* *##foreach($param in $method.Parameters.Value s) #* *##set ($fk = false) #* *##set ($tempClass = $currentClass) #* *##foreach ($class in $currentProject.C lasses) #* *##if ($class.Name == $param.Type) #* *##set ($fk = true) #* *##set ($tempClass = $class) #* *##end #* *##end #* *##if ($fk == false) #* *# cmd.Parameters.Add(new NpgsqlPara meter("$param.FieldName", p.$param.PropertyName)); #* *##else #* *# cmd.Parameters.Add(new NpgsqlPara meter("$param.FieldName", p.${param.PropertyName}.Id)); #* *##end #* *##end cmd.ExecuteNonQuery(); trans.Commit(); } catch { trans.Rollback(); throw; } finally { conn.Close(); cmd.Dispose(); trans.Dispose(); conn.Dispose(); } } #**##end #else #**##set ($SqlCommand = $method.SqlCommand) #**##if ($currentClass.IsAbstract)
154
#* *##set ($methodHeader = "$method.Scope abst ract $method.ReturnType ${method.Name}(${currentClass.Name} p)") #**##elseif ($method.Override) #* *##set ($methodHeader = "$method.Scope over ride $method.ReturnType ${method.Name}(${currentClass.BaseClass.Name} p)") #**##else #* *##set ($methodHeader = "$method.Scope $met hod.ReturnType ${method.Name}(${currentClass.Name} p)") #**##end #**##if ($currentClass.IsAbstract) #**# ${methodHeader}; #**##else $methodHeader { string sqlCommand = @"$SqlCommand"; NpgsqlConnection conn = new NpgsqlConnectio n(STR_CONN); conn.Open(); NpgsqlCommand cmd = new NpgsqlCommand(sqlCo mmand, conn); NpgsqlDataAdapter adapter = new NpgsqlDataA dapter(cmd); DataTable dt = new DataTable(); try { #* *##foreach($param in $method.Parameters.Val ues) cmd.Parameters.Add(new NpgsqlParameter( "$param.FieldName", p.$param.PropertyName)); #* *##end adapter.Fill(dt); if (dt.Rows.Count > 0) { #* *##if ((!$method.Override && $method.Re turnType == "${currentClass.Name}") || ($method.Override && $me thod.ReturnType == "${currentClass.BaseClass.Name}")) ${currentClass.Name} obj = new ${cu rrentClass.Name}(); DataRow reg = dt.Rows[0]; #* *##foreach($attribute in $currentCl ass.Fields.Values) #* *##set ($fk = false) #* *##set ($tempClass = $currentCl ass) #* *##foreach ($class in $currentP roject.Classes) #* *##if ($class.Name == $attr ibute.Type) #* *##set ($fk = true) #* *##set ($tempClass = $c lass) #* *##end #* *##end #* *##if ($fk == false) #* *# obj.$attribute.PropertyNa me = (${attribute.Type})reg["$attribute.FieldName"]; #* *##else #* *##set ($attr = $attribute) #* *##foreach ($tempAttribute in $tempClass.Fields.Values) #* *##if ($tempAttribute.F ield.IsPrimaryKey) #* *##set ($attr = $te mpAttribute) #* *##end #* *##end #* *# $attribute.PropertyName _ $attribute.PropertyName = new ${attribute.PropertyName}(); #* *# _${attribute.PropertyName }.$attr.PropertyName = (${attr.Type})reg["$attribute.FieldName"]; #* *# ${attribute.PropertyName} DAO _${attribute.PropertyName}DAO = new ${attribute.Pro pertyName}DAO(); #* *# obj.$attribute.PropertyNa me = (${attribute.PropertyName})_${attribute.PropertyNam e}DAO.SelecionarPorId(_$tempClass.Name); #* *##end #* *##end #* *# return obj; #* *##else
155
#* *# return dt; #* *##end } else { return null; } } catch { throw; } finally { conn.Close(); cmd.Dispose(); adapter.Dispose(); conn.Dispose(); } } #**##end #end #end }
156
D TEMPLATES VB.NET
D.1 DTO TEMPLATE #set ($header = "Public") #if ($currentClass.IsAbstract) #set ($header = "$header MustInherit") #end #set ($header = "$header Class $currentClass.Name") #if ($currentClass.BaseClass.Name != "") #set ($header = "$header: Inherits $currentClass.Ba seClass.Name") #end $header #Region "Attributes" #**##foreach($attribute in $currentClass.Fields.Val ues) #**##if ($attribute.IsGenerated) #**# $attribute.Scope $attribute.Name As $attrib ute.Type #**##end #**##end #End Region #Region "Properties" #foreach($attribute in $currentClass.Fields.Val ues) #if ($attribute.IsGenerated) $attribute.Scope Property ${attribute.PropertyN ame}() As $attribute.Type Get Return $attribute.Name End Get Set (ByVal value As ${attribute.Type}) $attribute.Name = value End Set End Property #end #end #End Region End Class
D.2 DAO TEMPLATE imports System.Data imports Npgsql #set ($header = "public") #if ($currentClass.IsAbstract) #set ($header = "$header MustInherit") #end #set ($header = "$header Class ${currentClass.Name} DAO") #if ($currentClass.BaseClass.Name != "") #set ($header = "$header: Inherits ${currentClass.B aseClass.Name}DAO") #end $header #**##if ($currentClass.IsAbstract) #**# Protected Shared STR_CONN As String = "$currentProject.Database.ConnectionSettings.Connec tionString" #**##elseif ($currentClass.BaseClass.Name != "") #**##else #**# Protected Shared STR_CONN As String = "$currentProject.Database.ConnectionSettings.Connec tionString" #**##end
157
#foreach($method in $currentClass.Methods) #if ($method.TypeAsString == "Insert" || $method.Ty peAsString == "Update" || $method.TypeAsString == "Delete") #**##set ($SqlCommand = $method.SqlCommand) #**##if ($currentClass.IsAbstract) #* *##set ($methodHeader = "$method.Scope Must Override Sub ${method.Name}(p As ${currentClass.Name})") #**##elseif ($method.Override) #* *##set ($methodHeader = "$method.Scope Over rides Sub ${method.Name}(p As ${currentClass.BaseClass.Name})") #**##else #* *##set ($methodHeader = "$method.Scope Sub ${method.Name}(p As ${currentClass.Name})") #**##end #**# ${methodHeader} #**##if (!${currentClass.IsAbstract}) Dim sqlCommand As String = "${SqlCommand}" Dim conn As New NpgsqlConnection(STR_CONN) conn.Open() Dim trans As NpgsqlTransaction = conn.Begin Transaction() Dim cmd As New NpgsqlCommand(sqlCommand, co nn, trans) Try #* *##foreach($param in $method.Parameters.Value s) #* *##set ($fk = false) #* *##set ($tempClass = $currentClass) #* *##foreach ($class in $currentProject.C lasses) #* *##if ($class.Name == $param.Type) #* *##set ($fk = true) #* *##set ($tempClass = $class) #* *##end #* *##end #* *##if ($fk == false) #* *# cmd.Parameters.Add(New NpgsqlPara meter("$param.FieldName", p.$param.PropertyName)) #* *##else #* *# cmd.Parameters.Add(New NpgsqlPara meter("$param.FieldName", p.${param.PropertyName}.Id)) #* *##end #* *##end cmd.ExecuteNonQuery() trans.Commit() Catch trans.Rollback() Throw Finally conn.Close() cmd.Dispose() trans.Dispose() conn.Dispose() End Try End Sub #**##end #else #**##set ($SqlCommand = $method.SqlCommand) #**##if ($currentClass.IsAbstract) #* *##set ($methodHeader = "$method.Scope Must Override Function ${method.Name}(p As ${currentClass.Name}) As $metho d.ReturnType") #**##elseif ($method.Override) #* *##set ($methodHeader = "$method.Scope Over rides Function ${method.Name}(p As ${currentClass.BaseClass.Name}) As $method.ReturnType") #**##else #* *##set ($methodHeader = "$method.Scope Func tion ${method.Name}(p As ${currentClass.Name}) As $method.ReturnType") #**##end $methodHeader #**##if (!${currentClass.IsAbstract}) Dim sqlCommand As String = "$SqlCommand"
158
Dim conn As New NpgsqlConnection(STR_CONN) conn.Open() Dim cmd As New NpgsqlCommand(sqlCommand, co nn) Dim adapter As New NpgsqlDataAdapter(cmd) Dim dt As New DataTable() Try #* *##foreach($param in $method.Parameters.Val ues) cmd.Parameters.Add(new NpgsqlParameter( "$param.FieldName", p.$param.PropertyName)) #* *##end adapter.Fill(dt) If (dt.Rows.Count > 0) Then #* *##if ((!$method.Override && $method.Re turnType == "${currentClass.Name}") || ($method.Override && $me thod.ReturnType == "${currentClass.BaseClass.Name}")) Dim obj As New ${currentClass.Name} () Dim reg As DataRow = dt.Rows(0) #* *##foreach($attribute in $currentCl ass.Fields.Values) #* *##set ($fk = false) #* *##set ($tempClass = $currentCl ass) #* *##foreach ($class in $currentP roject.Classes) #* *##if ($class.Name == $attr ibute.Type) #* *##set ($fk = true) #* *##set ($tempClass = $c lass) #* *##end #* *##end #* *##if ($fk == false) #* *# obj.$attribute.PropertyNa me = CType(reg("$attribute.FieldName"), ${attribute.Type }) #* *##else #* *##set ($attr = $attribute) #* *##foreach ($tempAttribute in $tempClass.Fields.Values) #* *##if ($tempAttribute.F ield.IsPrimaryKey) #* *##set ($attr = $te mpAttribute) #* *##end #* *##end #* *# Dim _$attribute.PropertyN ame As new ${attribute.PropertyName}() #* *# _${attribute.PropertyName }.$attr.PropertyName = CType(reg("$attribute.FieldName"), ${attr.Type}) #* *# Dim _${attribute.Property Name}DAO As new ${attribute.PropertyName}DAO() #* *# obj.$attribute.PropertyNa me = CType(_${attribute.PropertyName}DAO.SelecionarPorId (_$tempClass.Name), ${attribute.PropertyName}) #* *##end #* *##end #* *# Return obj #* *##else #* *# Return dt #* *##end Else Return Nothing End If Catch Throw Finally conn.Close() cmd.Dispose() adapter.Dispose() conn.Dispose() End Try End Function #**##end #end #end End Class
159
160
E CÓDIGO-FONTE C#
E.1 PESSOA using System; public abstract class Pessoa { #region Attributes public int id; public String nome; public String cpf; #endregion #region Properties public int Id { get { return id; } set { id = value; } } public String Nome { get { return nome; } set { nome = value; } } public String Cpf { get { return cpf; } set { cpf = value; } } #endregion }
E.2 ALUNO using System; public class Aluno: Pessoa { #region Attributes #endregion #region Properties #endregion }
E.3 PROFESSOR using System; public class Professor: Pessoa { #region Attributes
161
#endregion #region Properties #endregion }
E.4 TCC using System; public class Tcc { #region Attributes public int id; public String titulo; public Aluno aluno; public Professor professor; #endregion #region Properties public int Id { get { return id; } set { id = value; } } public String Titulo { get { return titulo; } set { titulo = value; } } public Aluno Aluno { get { return aluno; } set { aluno = value; } } public Professor Professor { get { return professor; } set { professor = value; } } #endregion }
E.5 PESSOADAO using System; using System.Data; using Npgsql; public abstract class PessoaDAO { protected const string STR_CONN = @"SERVER=localhost;PORT=5432;DATABASE=TCCSample;USE R ID=postgres;PASSWORD=1234"; public abstract Pessoa SelecionarPorId(Pessoa p );
162
}
E.6 ALUNODAO using System; using System.Data; using Npgsql; public class AlunoDAO: PessoaDAO { public override Pessoa SelecionarPorId(Pessoa p ) { string sqlCommand = @"SELECT * FROM aluno W HERE id = @id;"; NpgsqlConnection conn = new NpgsqlConnectio n(STR_CONN); conn.Open(); NpgsqlCommand cmd = new NpgsqlCommand(sqlCo mmand, conn); NpgsqlDataAdapter adapter = new NpgsqlDataA dapter(cmd); DataTable dt = new DataTable(); try { cmd.Parameters.Add(new NpgsqlParameter( "id", p.Id)); adapter.Fill(dt); if (dt.Rows.Count > 0) { Aluno obj = new Aluno(); DataRow reg = dt.Rows[0]; obj.Id = (int)reg["id"]; obj.Nome = (String)reg["nome"]; obj.Cpf = (String)reg["cpf"]; return obj; } else { return null; } } catch { throw; } finally { conn.Close(); cmd.Dispose(); adapter.Dispose(); conn.Dispose(); } } }
E.7 PROFESSORDAO using System; using System.Data; using Npgsql; public class ProfessorDAO: PessoaDAO { public override Pessoa SelecionarPorId(Pessoa p ) { string sqlCommand = @"SELECT * FROM profess or WHERE id = @id;"; NpgsqlConnection conn = new NpgsqlConnectio n(STR_CONN); conn.Open();
163
NpgsqlCommand cmd = new NpgsqlCommand(sqlCo mmand, conn); NpgsqlDataAdapter adapter = new NpgsqlDataA dapter(cmd); DataTable dt = new DataTable(); try { cmd.Parameters.Add(new NpgsqlParameter( "id", p.Id)); adapter.Fill(dt); if (dt.Rows.Count > 0) { Professor obj = new Professor(); DataRow reg = dt.Rows[0]; obj.Id = (int)reg["id"]; obj.Nome = (String)reg["nome"]; obj.Cpf = (String)reg["cpf"]; return obj; } else { return null; } } catch { throw; } finally { conn.Close(); cmd.Dispose(); adapter.Dispose(); conn.Dispose(); } } }
E.8 TCCDAO using System; using System.Data; using Npgsql; public class TccDAO { private const string STR_CONN = @"SERVER=localhost;PORT=5432;DATABASE=TCCSample;USE R ID=postgres;PASSWORD=1234"; public void Alterar(Tcc p) { string sqlCommand = @"UPDATE tcc SET titulo = @titulo, id_aluno = @id_aluno, id_professor = @id_professor WHERE id = @id;"; NpgsqlConnection conn = new NpgsqlConnectio n(STR_CONN); conn.Open(); NpgsqlTransaction trans = conn.BeginTransac tion(); NpgsqlCommand cmd = new NpgsqlCommand(sqlCo mmand, conn, trans); try { cmd.Parameters.Add(new NpgsqlParameter( "id", p.Id)); cmd.Parameters.Add(new NpgsqlParameter( "titulo", p.Titulo)); cmd.Parameters.Add(new NpgsqlParameter( "id_aluno", p.Aluno.Id)); cmd.Parameters.Add(new NpgsqlParameter( "id_professor", p.Professor.Id)); cmd.ExecuteNonQuery(); trans.Commit(); } catch { trans.Rollback();
164
throw; } finally { conn.Close(); cmd.Dispose(); trans.Dispose(); conn.Dispose(); } } public void Excluir(Tcc p) { string sqlCommand = @"DELETE FROM tcc WHERE id = @id;"; NpgsqlConnection conn = new NpgsqlConnectio n(STR_CONN); conn.Open(); NpgsqlTransaction trans = conn.BeginTransac tion(); NpgsqlCommand cmd = new NpgsqlCommand(sqlCo mmand, conn, trans); try { cmd.Parameters.Add(new NpgsqlParameter( "id", p.Id)); cmd.ExecuteNonQuery(); trans.Commit(); } catch { trans.Rollback(); throw; } finally { conn.Close(); cmd.Dispose(); trans.Dispose(); conn.Dispose(); } } public void Inserir(Tcc p) { string sqlCommand = @"INSERT INTO tcc (id, titulo, id_aluno, id_professor) VALUES (@id, @titulo, @id_aluno, @id_ professor);"; NpgsqlConnection conn = new NpgsqlConnectio n(STR_CONN); conn.Open(); NpgsqlTransaction trans = conn.BeginTransac tion(); NpgsqlCommand cmd = new NpgsqlCommand(sqlCo mmand, conn, trans); try { cmd.Parameters.Add(new NpgsqlParameter( "id", p.Id)); cmd.Parameters.Add(new NpgsqlParameter( "titulo", p.Titulo)); cmd.Parameters.Add(new NpgsqlParameter( "id_aluno", p.Aluno.Id)); cmd.Parameters.Add(new NpgsqlParameter( "id_professor", p.Professor.Id)); cmd.ExecuteNonQuery(); trans.Commit(); } catch { trans.Rollback(); throw; } finally { conn.Close(); cmd.Dispose(); trans.Dispose(); conn.Dispose(); }
165
} public Tcc SelecionarPorTitulo(Tcc p) { string sqlCommand = @"SELECT * FROM tcc WHE RE titulo = @titulo;"; NpgsqlConnection conn = new NpgsqlConnectio n(STR_CONN); conn.Open(); NpgsqlCommand cmd = new NpgsqlCommand(sqlCo mmand, conn); NpgsqlDataAdapter adapter = new NpgsqlDataA dapter(cmd); DataTable dt = new DataTable(); try { cmd.Parameters.Add(new NpgsqlParameter( "titulo", p.Titulo)); adapter.Fill(dt); if (dt.Rows.Count > 0) { Tcc obj = new Tcc(); DataRow reg = dt.Rows[0]; obj.Id = (int)reg["id"]; obj.Titulo = (String)reg["titulo"]; Aluno _Aluno = new Aluno(); _Aluno.Id = (int)reg["id_aluno"]; AlunoDAO _AlunoDAO = new AlunoDAO() ; obj.Aluno = (Aluno)_AlunoDAO.Seleci onarPorId(_Aluno); Professor _Professor = new Professo r(); _Professor.Id = (int)reg["id_profes sor"]; ProfessorDAO _ProfessorDAO = new Pr ofessorDAO(); obj.Professor = (Professor)_ProfessorDAO.SelecionarPorId(_Professor ); return obj; } else { return null; } } catch { throw; } finally { conn.Close(); cmd.Dispose(); adapter.Dispose(); conn.Dispose(); } } public DataTable SelecionarTodos(Tcc p) { string sqlCommand = @"SELECT * FROM tcc;"; NpgsqlConnection conn = new NpgsqlConnectio n(STR_CONN); conn.Open(); NpgsqlCommand cmd = new NpgsqlCommand(sqlCo mmand, conn); NpgsqlDataAdapter adapter = new NpgsqlDataA dapter(cmd); DataTable dt = new DataTable(); try { adapter.Fill(dt); if (dt.Rows.Count > 0) { return dt; } else {
166
return null; } } catch { throw; } finally { conn.Close(); cmd.Dispose(); adapter.Dispose(); conn.Dispose(); } } public Tcc SelecionarPorId(Tcc p) { string sqlCommand = @"SELECT * FROM tcc WHE RE id = @id;"; NpgsqlConnection conn = new NpgsqlConnectio n(STR_CONN); conn.Open(); NpgsqlCommand cmd = new NpgsqlCommand(sqlCo mmand, conn); NpgsqlDataAdapter adapter = new NpgsqlDataA dapter(cmd); DataTable dt = new DataTable(); try { cmd.Parameters.Add(new NpgsqlParameter( "id", p.Id)); adapter.Fill(dt); if (dt.Rows.Count > 0) { Tcc obj = new Tcc(); DataRow reg = dt.Rows[0]; obj.Id = (int)reg["id"]; obj.Titulo = (String)reg["titulo"]; Aluno _Aluno = new Aluno(); _Aluno.Id = (int)reg["id_aluno"]; AlunoDAO _AlunoDAO = new AlunoDAO() ; obj.Aluno = (Aluno)_AlunoDAO.Seleci onarPorId(_Aluno); Professor _Professor = new Professo r(); _Professor.Id = (int)reg["id_profes sor"]; ProfessorDAO _ProfessorDAO = new Pr ofessorDAO(); obj.Professor = (Professor)_ProfessorDAO.SelecionarPorId(_Professor ); return obj; } else { return null; } } catch { throw; } finally { conn.Close(); cmd.Dispose(); adapter.Dispose(); conn.Dispose(); } } }
167
F CÓDIGO-FONTE VB.NET
F.1 PESSOA Public MustInherit Class Pessoa #Region "Attributes" public _id As Integer public _nome As String public _cpf As String #End Region #Region "Properties" public Property Id() As Integer Get Return _id End Get Set (ByVal value As Integer) _id = value End Set End Property public Property Nome() As String Get Return _nome End Get Set (ByVal value As String) _nome = value End Set End Property public Property Cpf() As String Get Return _cpf End Get Set (ByVal value As String) _cpf = value End Set End Property #End Region End Class
F.2 ALUNO Public Class Aluno: Inherits Pessoa #Region "Attributes" #End Region #Region "Properties" #End Region End Class
F.3 PROFESSOR Public Class Professor: Inherits Pessoa #Region "Attributes"
168
#End Region #Region "Properties" #End Region End Class
F.4 TCC Public Class Tcc #Region "Attributes" public _id As Integer public _titulo As String public _aluno As Aluno public _professor As Professor #End Region #Region "Properties" public Property Id() As Integer Get Return _id End Get Set (ByVal value As Integer) _id = value End Set End Property public Property Titulo() As String Get Return _titulo End Get Set (ByVal value As String) _titulo = value End Set End Property public Property Aluno() As Aluno Get Return _aluno End Get Set (ByVal value As Aluno) _aluno = value End Set End Property public Property Professor() As Professor Get Return _professor End Get Set (ByVal value As Professor) _professor = value End Set End Property #End Region End Class
F.5 PESSOADAO imports System.Data imports Npgsql
169
public MustInherit Class PessoaDAO Protected Shared STR_CONN As String = "SERVER=localhost;PORT=5432;DATABASE=TCCSample;USER ID=postgres;PASSWORD=1234" public MustOverride Function SelecionarPorId(p As Pessoa) As Pessoa End Class
F.6 ALUNODAO imports System.Data imports Npgsql public Class AlunoDAO: Inherits PessoaDAO public Overrides Function SelecionarPorId(p As Pessoa) As Pessoa Dim sqlCommand As String = "SELECT * FROM a luno WHERE id = @id;" Dim conn As New NpgsqlConnection(STR_CONN) conn.Open() Dim cmd As New NpgsqlCommand(sqlCommand, co nn) Dim adapter As New NpgsqlDataAdapter(cmd) Dim dt As New DataTable() Try cmd.Parameters.Add(new NpgsqlParameter( "id", p.Id)) adapter.Fill(dt) If (dt.Rows.Count > 0) Then Dim obj As New Aluno() Dim reg As DataRow = dt.Rows(0) obj.Id = CType(reg("id"), Integer) obj.Nome = CType(reg("nome"), Strin g) obj.Cpf = CType(reg("cpf"), String) Return obj Else Return Nothing End If Catch Throw Finally conn.Close() cmd.Dispose() adapter.Dispose() conn.Dispose() End Try End Function End Class
F.7 PROFESSORDAO imports System.Data imports Npgsql public Class ProfessorDAO: Inherits PessoaDAO public Overrides Function SelecionarPorId(p As Pessoa) As Pessoa Dim sqlCommand As String = "SELECT * FROM p rofessor WHERE id = @id;" Dim conn As New NpgsqlConnection(STR_CONN) conn.Open() Dim cmd As New NpgsqlCommand(sqlCommand, co nn) Dim adapter As New NpgsqlDataAdapter(cmd) Dim dt As New DataTable() Try cmd.Parameters.Add(new NpgsqlParameter( "id", p.Id)) adapter.Fill(dt) If (dt.Rows.Count > 0) Then Dim obj As New Professor()
170
Dim reg As DataRow = dt.Rows(0) obj.Id = CType(reg("id"), Integer) obj.Nome = CType(reg("nome"), Strin g) obj.Cpf = CType(reg("cpf"), String) Return obj Else Return Nothing End If Catch Throw Finally conn.Close() cmd.Dispose() adapter.Dispose() conn.Dispose() End Try End Function End Class
F.8 TCCDAO imports System.Data imports Npgsql public Class TccDAO Protected Shared STR_CONN As String = "SERVER=localhost;PORT=5432;DATABASE=TCCSample;USER ID=postgres;PASSWORD=1234" public Sub Alterar(p As Tcc) Dim sqlCommand As String = "UPDATE tcc SET titulo = @titulo, id_aluno = @id_aluno, id_professor = @id_professor WHERE id = @id;" Dim conn As New NpgsqlConnection(STR_CONN) conn.Open() Dim trans As NpgsqlTransaction = conn.Begin Transaction() Dim cmd As New NpgsqlCommand(sqlCommand, co nn, trans) Try cmd.Parameters.Add(New NpgsqlParameter( "id", p.Id)) cmd.Parameters.Add(New NpgsqlParameter( "titulo", p.Titulo)) cmd.Parameters.Add(New NpgsqlParameter( "id_aluno", p.Aluno.Id)) cmd.Parameters.Add(New NpgsqlParameter( "id_professor", p.Professor.Id)) cmd.ExecuteNonQuery() trans.Commit() Catch trans.Rollback() Throw Finally conn.Close() cmd.Dispose() trans.Dispose() conn.Dispose() End Try End Sub public Sub Excluir(p As Tcc) Dim sqlCommand As String = "DELETE FROM tcc WHERE id = @id;" Dim conn As New NpgsqlConnection(STR_CONN) conn.Open() Dim trans As NpgsqlTransaction = conn.Begin Transaction() Dim cmd As New NpgsqlCommand(sqlCommand, co nn, trans) Try cmd.Parameters.Add(New NpgsqlParameter( "id", p.Id)) cmd.ExecuteNonQuery() trans.Commit() Catch trans.Rollback() Throw
171
Finally conn.Close() cmd.Dispose() trans.Dispose() conn.Dispose() End Try End Sub public Sub Inserir(p As Tcc) Dim sqlCommand As String = "INSERT INTO tcc (id, titulo, id_aluno, id_professor) VALUES (@id, @titulo, @id_aluno, @id_ professor);" Dim conn As New NpgsqlConnection(STR_CONN) conn.Open() Dim trans As NpgsqlTransaction = conn.Begin Transaction() Dim cmd As New NpgsqlCommand(sqlCommand, co nn, trans) Try cmd.Parameters.Add(New NpgsqlParameter( "id", p.Id)) cmd.Parameters.Add(New NpgsqlParameter( "titulo", p.Titulo)) cmd.Parameters.Add(New NpgsqlParameter( "id_aluno", p.Aluno.Id)) cmd.Parameters.Add(New NpgsqlParameter( "id_professor", p.Professor.Id)) cmd.ExecuteNonQuery() trans.Commit() Catch trans.Rollback() Throw Finally conn.Close() cmd.Dispose() trans.Dispose() conn.Dispose() End Try End Sub public Function SelecionarPorTitulo(p As Tcc) A s Tcc Dim sqlCommand As String = "SELECT * FROM t cc WHERE titulo = @titulo;" Dim conn As New NpgsqlConnection(STR_CONN) conn.Open() Dim cmd As New NpgsqlCommand(sqlCommand, co nn) Dim adapter As New NpgsqlDataAdapter(cmd) Dim dt As New DataTable() Try cmd.Parameters.Add(new NpgsqlParameter( "titulo", p.Titulo)) adapter.Fill(dt) If (dt.Rows.Count > 0) Then Dim obj As New Tcc() Dim reg As DataRow = dt.Rows(0) obj.Id = CType(reg("id"), Integer) obj.Titulo = CType(reg("titulo"), S tring) Dim _Aluno As new Aluno() _Aluno.Id = CType(reg("id_aluno"), Integer) Dim _AlunoDAO As new AlunoDAO() obj.Aluno = CType(_AlunoDAO.Selecio narPorId(_Aluno), Aluno) Dim _Professor As new Professor() _Professor.Id = CType(reg("id_profe ssor"), Integer) Dim _ProfessorDAO As new ProfessorD AO() obj.Professor = CType(_ProfessorDAO .SelecionarPorId(_Professor), Professor) Return obj Else Return Nothing End If Catch Throw Finally conn.Close()
172
cmd.Dispose() adapter.Dispose() conn.Dispose() End Try End Function public Function SelecionarTodos(p As Tcc) As Da taTable Dim sqlCommand As String = "SELECT * FROM t cc;" Dim conn As New NpgsqlConnection(STR_CONN) conn.Open() Dim cmd As New NpgsqlCommand(sqlCommand, co nn) Dim adapter As New NpgsqlDataAdapter(cmd) Dim dt As New DataTable() Try adapter.Fill(dt) If (dt.Rows.Count > 0) Then Return dt Else Return Nothing End If Catch Throw Finally conn.Close() cmd.Dispose() adapter.Dispose() conn.Dispose() End Try End Function public Function SelecionarPorId(p As Tcc) As Tc c Dim sqlCommand As String = "SELECT * FROM t cc WHERE id = @id;" Dim conn As New NpgsqlConnection(STR_CONN) conn.Open() Dim cmd As New NpgsqlCommand(sqlCommand, co nn) Dim adapter As New NpgsqlDataAdapter(cmd) Dim dt As New DataTable() Try cmd.Parameters.Add(new NpgsqlParameter( "id", p.Id)) adapter.Fill(dt) If (dt.Rows.Count > 0) Then Dim obj As New Tcc() Dim reg As DataRow = dt.Rows(0) obj.Id = CType(reg("id"), Integer) obj.Titulo = CType(reg("titulo"), S tring) Dim _Aluno As new Aluno() _Aluno.Id = CType(reg("id_aluno"), Integer) Dim _AlunoDAO As new AlunoDAO() obj.Aluno = CType(_AlunoDAO.Selecio narPorId(_Aluno), Aluno) Dim _Professor As new Professor() _Professor.Id = CType(reg("id_profe ssor"), Integer) Dim _ProfessorDAO As new ProfessorD AO() obj.Professor = CType(_ProfessorDAO .SelecionarPorId(_Professor), Professor) Return obj Else Return Nothing End If Catch Throw Finally conn.Close() cmd.Dispose() adapter.Dispose() conn.Dispose() End Try
173
End Function End Class