Design Patterns no Delphi 7
Usando ModelMaker
6.2
A tecnologia de desenvolvimento de aplicações
evoluiu muito nos últimos anos, desde que surgiram as primeiras ferramentas RAD
no mercado. Os objetivos principais das ferramentas RAD eram sempre otimizar,
aperfeiçoar e reutilizar código no processo de engenharia de software.
Mas como tudo em tecnologia passa rápido, hoje temos
uma nova visão de engenharia de software. Quando desenvolvemos algo, devemos
analisar sempre o contexto do problema, desenhar as partes relacionadas e
construir um modelo lógico e funcional, principalmente, orientado a objetos.
Baseado nesta visão é que foram construídas certas técnicas de construções de
projetos de software, e logo tais técnicas receberam um nome: UML (Linguagem de
Modelagem Unificada).
Logo, o que é a UML? Devemos partir do seguinte
principio. Todo projeto de software parte de uma especificação (Análise de
Requisitos, Modelagem de Entidades e suas relações, e Análise de Processos e
Ações de Objetos), logo, tal especificação está presente em qualquer projeto de
software, portanto UML é a forma “Unificada” e se tratar tais conceitos que já
utilizávamos há muito tempo, só que com algumas modificações.
Uma é de tornar um projeto (Gráficos, Diagramas Etc)
padrão, ou seja, acabar com todos aqueles problemas de analistas falando
línguas diferentes dos programadores ou de outros analistas. A outra é que os
focos destes projetos sejam voltados firmemente para a estrutura orientada a
objetos, pois tal estrutura mostrou seu sucesso e eficácia ao longo dos anos.
Um dos maiores problemas com a orientação a objetos,
é que nós, desenvolvedores, queremos sempre ter o controle e poder em mãos só
que num curto espaço de tempo, ou seja, queremos desenvolver aplicações rápidas
(RAD) com o auxilio máximo de ferramentas de ultima geração como o Delphi. Pela
natureza RAD dessas ferramentas, elas nos levam e gerar programas bons, mas sem
nenhuma especificação, completamente fora dos padrões de orientação a objetos.
Um dos
fatores que levam um desenvolvedor a falhar em um projeto de software orientado
a objeto é de não conhecer completamente a arquitetura, ou de conhecer um
pouco, mas sentir “preguiça” de implementá-la (Quem nunca deixou de implementar
um construtor em uma classe ou redefinir métodos polimórficos). Bom, conhecer a
especificação de orientação a objetos é um pouco cansativo, mas para isso,
foram criados alguns padrões de modelagem orientados a objetos, presentes em
qualquer projeto de software.
Tais padrões foram criados, porque alguns
engenheiros de software enfrentaram tais problemas no passado e quiseram criar
algo que ajudasse futuros projetos, fazendo com que as pessoas que fosse criar
tais projetos não passassem por tudo que eles passaram. Bem, logo o que são
Design Patterns? São padrões de engenharia de software, que resolvem alguns
problemas comuns encontrados na maioria (Senão todos) os projetos de software.
Para cada problema em especifico, existe um Pattern (Padrão), compostos por
alguns trechos de código que são implementados como métodos e variáveis de
instância em classes.
O conceito de Design Patterns não é novo, visto que
em ambientes como Java e C++ já estão sendo implementados (e tem dado certo!).
O que acontece é que nós, desenvolvedores Delphi (RAD) nos acostumamos a
extrair o máximo de RAD da ferramenta e esquecer do bom e velho projeto de
software bem implementado.
Apresentação das Ferramentas
A boa nova (bem não tão nova), é que o Delphi 7 vem
agora com um companheiro ideal para tais problemas, que é o ModelMaker 6.2.
Este aplicativo, vem junto com o pacote Borland Delphi 7 Enterprise ou
Arquitect, e o melhor de tudo, numa versão especial do aplicativo, adaptada
para se integrar completamente com o Delphi, fornecendo API’S e Interfaces que
fazem a comunicação dos dois. Isso fornece um ambiente robusto possibilitando:
-
Geração
de código automática a partir de diagramas UML
-
Geração
de Projetos UML a partir de código fonte PASCAL
-
Produtividade
e eficiência na escrita de código orientado a objetos.
-
Geração
de diagramas e modelos ao invés de puro código
-
Documentação
de Unit’s Delphi e geração de arquivos Help
-
Uso
de Design Patterns em Delphi
Sendo que este último item é realmente um novo
começo para aqueles programadores antigos do bom e velho PASCAL, ou até de
linguagens procedurais como Clipper.
Neste artigo veremos como trabalhar esta ferramenta,
e como implementar eficientemente estes conceitos novos dentro do Delphi. Para
implementar este exemplo, você precisará do Delphi 7 (Enterprise ou Arquitect),
do ModelMaker 6.2 instalado. Caso você possua o Delphi 6 ou anterior, você
poderá baixar o ModelMaker para sua versão especifica do Delphi no site www.modelmakertools.com. Em versões
anteriores, as interfaces do ModelMaker poderão ser diferentes das apresentadas
neste artigo.
Conhecendo de perto os
Design Patterns
Um design pattern é uma solução genérica para
determinado problema, uma espécie de receita de bolo para resolver determinado
problema. Estas soluções foram testadas e aprovadas por vários programadores
experientes e, portanto é sempre bem aplicado em problemas gerais na engenharia
de software.
Design Patterns provém soluções reutilizáveis para
um determinado sistema, eles te ajudam a escolher entre técnicas alternativas,
sem fazer seu projeto ser “amarrado” a alguma tecnologia ou linguagem
específica. A idéia por traz dos design patterns, é que você ao desenvolver uma
aplicação, faça 70% de seu projeto somente aplicando patterns e 30%
programando.
Neste aspecto, os patterns são obviamente bem vindos
no mundo da programação RAD, entretanto, é de se esperar que dois aspectos
sejam claramente estabelecidos. Um, é que o desenvolvedor deve conhecer muitos
bem os patterns que trabalha. Dois, em determinado momento, o desenvolvedor se
verá na situação de remover um pattern de determinada parte do projeto, e nem sempre,
esta atitude nos leva a um resultado satisfatório, visto que escrevemos código
que muita das vezes são “amarrados”, ou seja, as partes de um módulo dependem
de outros, e se estes outros módulos forem removidos, todos aqueles módulos que
dependiam dele deixarão de funcionar corretamente.
Isso nos leva a crer que quando se trabalha com
Design Patterns, não se pode aplicá-los arbitrariamente, só porque a idéia por
traz de determinado pattern parece fazer sentido no momento em que ele foi
aplicado. Devemos sempre analisar se a aplicação de determinado pattern não
acarretará em problemas futuros, ou que não iremos, por algum motivo nos
desfazer daquele pattern.
Um detalhe bastante interessante dos patterns, é que
são notações de software que nós programadores já utilizamos, só não sabíamos
do que se tratava. Portanto é quase que automático o aprendizado de design
patterns, pois temos que em ultima análise, dar nomes as técnicas já
conhecidas.
Outra questão importante no uso de Design Patterns,
é que um pattern é responsável por tudo àquilo que ele abrange (classes,
métodos, implementação de métodos etc). Portanto, se alguma alteração deve ser
feita na implementação de algum pattern, esta deve ser feita na definição do
pattern, e não onde ele foi aplicado. Isso significa que você deve sempre, ao
alterar um pattern, modificar o original e não no local específico onde ele foi
aplicado. Este é um conceito de programação chamado modelo Proxy, que
possibilita módulos de código serem mais reutilizáveis, e menos dispendiosos
para manutenção.
Para melhor entender os Design Pattern, iremos agora
ver cada um deles, e analisar como aplica-los eficientemente na linguagem
Delphi (Sei que isso pode ter soado estranho, mas a partir do Delphi 7, não se
chama mais linguagem Object Pascal, e sim Delphi Language).
Vamos então dar uma olhada em alguns dos principais
patterns.
·
Wrapper Antes chamado de Adapter. O objetivo do pattern wrapper é remodelar
determinada classe para que ela possa ser utilizável em determinado aspecto numa
cadeia de herança, fazendo acesso direto a determinada classe base, ou seja, a
capacidade de você poder tirar proveito de uma classe sem ter que
necessariamente herdar desta classe, apenas manter uma referência interna, na
forma de atributo. Se refere ao conceito de composição da UML.
·
Mediator Tem como objetivo remover o excesso de acoplamento entre um conjunto
de classes. Durante o desenvolvimento de um framework orientado a objetos,
existe uma forte probabilidade de você tornar seu código pouco reutilizável,
devido ao excesso de acoplamento (classes que dependem de outras para
funcionar). O pattern mediator realiza esta tarefa usando um mecanismo de
delegação de tarefas via eventos, ou seja, este pattern implementa eventos em
sua classe e se responsabiliza em disparar estes eventos, quando for necessário
utilizar outras classes.
·
Singleton Garante que a classe que receba este pattern terá durante o ciclo de
vida de uma aplicação uma única instância em produção. Isto significa que para
esta classe existirá somente um objeto instanciado. Ele realiza este processo,
implementando alguns métodos que garantem determinado numero de instâncias em
memória, no caso uma.
·
Decorator Tem como objetivo implementar novas funcionalidades em determinado
objeto automaticamente. Baseado nos conceitos de OOP, para que determinada
classe possa estender suas funcionalidades, devem criar uma nova classe que
herde da classe em questão. Mas quando se trata de objetos, a coisa muda de
foco, pois não podemos fazer herança em tempo de execução (Até pode, mas
definitivamente está fora de cogitação). Isso é feito através de polimorfismo
de classes.
·
Lock Este pattern prove um mecanismo para que determinada
classe possa ser travada, enquanto outras operações estejam sendo executadas,
ou seja, ela garante que a classe pode ser ativada e desativada
temporariamente, e forneça um mecanismo de verificação do status da classe. Um
bom exemplo do pattern Lock é imaginar um objeto cliente que possa ser “locked”
de realizar pedidos de compra, enquanto a atualização de estoque de algum
objeto produto esteja sendo feita.
·
Observer implementa uma dependência um – para – muitos
em uma classe para com outras classes, a fim de notificar as classes
dependentes do estado atual da classe referenciada. Isso possibilita que quando
o estado de determinada classe mudar, todas as classes “interessadas” será
notificado automaticamente, no mesmo momento. Isso é feito implementando uma
lista de objetos na classe e a implementação de rotinas get/set nas
propriedades da classe de tal forma que force uma notificação das classes da
lista, no momento da alteração de determinado atributo.
Um exemplo prático de
Patterns no ModelMaker
Para exemplificar o uso da
ferramenta ModelMaker na implementação de design patterns, iremos agora montar
um exemplo de como aplicar um pattern em uma classe criada no ModelMaker. O
exemplo que será demonstrado irá requerer a utilização do pattern wrapper, e
aproveitaremos para mostrar como fazer isso no ModelMaker.
Vale a pena lembrar que os
passo aqui mostrados para se implementar o pattern no ModelMaker será
basicamente o mesmo para outros patterns, diferenciando-se somente em quais
parâmetros determinado pattern requer para ser implementado.
Na linguagem Delphi, o polimorfismo é suportado
através de tipos de classe, e por suas interfaces implementadas. Isso faz com
que duas classes relacionadas possam responder de formas diferentes ao receber
a chamada de um mesmo método. Isso acontece somente se as classes estiverem
dentro de uma mesma hierarquia, ou seja, pertencerem a mesma cadeia de herança.
Às Vezes queremos que determinada classe faça algo polimórfico, mas ela não
pertence à hierarquia que deveria ter para implementar o polimorfismo. Nestes
casos, no Delphi fazemos então uma agregação, ou seja, colocamos uma classe
dentro da outra para poder acessar alguns de seus atributos ou métodos.
Por exemplo, imagine uma classe chamada TSample que
herda de TObject. Mas você quer que essa classe esteja na paleta de componentes
do Delphi. Bem, isso só é possível se ela herdar de TComponent, mas por alguma
razão não se pode fazer isso (Talvez porque essa classe seja um classe COM, por
exemplo). Para resolver este problema, podemos fazer o seguinte: Criamos uma
nova classe, TSampleWrapper, que herda de TComponent, e tem um objeto do tipo
TSample como uma de suas propriedades. Isso pode ser ilustrado no código a
seguir.
Antes:
type
TSample = class (TObject)
private
FSomeValue:
Integer;
public
function
SomeAction(const Data: string): Boolean;
property SomeValue: Integer read FSomeValue write FSomeValue;
end;
Depois:
TSampleWrapper = class(TComponent)
private
FSample:
TSample;
public
property Sample: TSample read FSample;
end;
Para finalizar nosso componente, podemos criar
“Wrappers” para as propriedades e métodos da classe TSample dentro da classe
TSampleWrapper, como ilustrado a seguir.
TSampleWrapper = class(TComponent)
private
FSample:
TSample;
protected
function GetSomeValue: Integer;
procedure SetSomeValue(Value: Integer);
public
function SomeAction(const Data: string): Boolean;
» property Sample: TSample read FSample;
property SomeValue: Integer read GetSomeValue write SetSomeValue;
end;
function
TSampleWrapper.GetSomeValue: Integer;
begin
Result :=
Sample.SomeValue;
end;
procedure
TSampleWrapper.SetSomeValue(Value: Integer);
begin
Sample.SomeValue := Value;
end;
function
TSampleWrapper.SomeAction(const
Data: string): Boolean;
begin
Result :=
Sample.SomeAction(Data);
end;
Neste exemplo, vimos como reaproveitar as
particularidades de uma classe, tomando por base outra classe como
implementadora. Em geral, no pattern Wrapper, são implementadas as seguintes
rotinas:
·
Todos
os membros de uma classe wrapper têm os mesmos atributos que a classe que está
sendo “Wrapped”.
·
Propriedades
têm seus especificadores de leitura e gravação implementados como métodos e não
como atributos.
·
Eventos
são manipulados pela classe wrapper usando eventos.
·
Métodos
são manipulados pela classe wrapper apenas chamando os métodos da classe base,
passando os devidos parâmetros se necessário, e recebendo os valores de retorno
que eles possivelmente retornem.
Como implementar o pattern
Wrapper no ModelMaker
Para implementar este exemplo, abra o Delphi caso
ele não esteja rodando, acione o menu “ModelMaker”, e a opção “Run ModelMaker”.
Isso abrirá o ModelMaker numa janela separada, com um projeto default, ou algum
projeto anterior usado em outra ocasião.
Dentro do ModelMaker, selecione o menu “New” e a
opção “New from default”. Esta opção cria um novo projeto de modelagem, com
algumas classes e interfaces padrões da VCL, como, por exemplo, a classe
TPersistent e TComponent, bem como é claro sua hierarquia, a classe TObject.
Antes de implementarmos o pattern, devemos criar as classes relacionadas
(TSample e TSampleWrapper). Para isso, clique com o botão direito do mouse na
janela “Classes View” que fica no painel superior a esquerda da janela.
Selecione a opção “Add Class” e será apresentada a janela a seguir:
|
|
Nesta
janela, devemos informar as características da classe que queremos criar, por
exemplo, o nome da classe, quem é o ancestral, no caso TObject e as
interfaces implementadas caso existam. Note que na caixa Ancestor, somente é
listado as classes que estão atualmente no modelo. Na caixa Class Name digite
TSample, e na caixa Ancestor escolha TObject. As outras opções podem ficar
como estão. Após isso clique em OK para confirmar a criação da classe dentro
do modelo. |
Agora devemos criar a classe TSampleWrapper seguindo
os mesmos passos só que agora a classe ancestral da classe que está sendo
criada, será TComponent. Repare como fica a organização das classes depois de
criadas:
|
|
As
classes são claramente visualizadas na forma hierárquica, através do TreeView
apresentado no Class View. Interfaces dentro do ModelMaker (Bem como na UML
em si) tem seu ícone representado por uma
“bolinha” e as classes são representadas por um quadrado. Isso
facilita na diferenciação de classes e interfaces a ajuda na criação de
diagrama de classes e diagrama de estados. Bem, para finalizar as etapas
preliminares a implementação do pattern Wrapper, deve criar a agregação de
classes no modelo criado, ou seja, devemos criar uma propriedade do tipo
TSample dentro da classe TSampleWrapper. Para que nós possamos implementar o
pattern dentro da classe TSampleWrapper. |
Para isso, selecione a classe TSampleWrapper no
“Classes View”, e clique no botão de adicionar propriedade na guia de utilitários da “Members View”
que fica logo abaixo do “Classes View”.

Nesta janela podemos criar uma nova propriedade para
a classe TSampleWraper. Na caixa name digite “Sample”, que será o nome da nossa
propriedade. Na caixa de visibility escolha “public” para que esta propriedade
possa ser acessada fora da classe. Na guia Data type esolha “class”, pois nossa
propriedade é realmente uma classe. Note que isto habilita a caixa Data type
Name, portanto devemos informar ao ModelMaker quem é a classe que representará
a propriedade. Escolha “TSample”. Nas guias Read Access e Write Access escolha
Field para que nossa propriedade aponte para um campo privado na classe TSampleWraper.
Após isso clique em OK para confirmar a criação da propriedade.
Agora vamos criar a propridade “SomeValue”
da classe TSample. Isso pode ser feito repetindo os mesmo passos que foram
feitos para a propriedade Sample da classe TSampleWrapper, porém devemos
informar na caixa Data type que se trata de uma propriedade do tipo “Integer”.
Os especificadores de “Read Access” e “Write Access” deverão ser do tipo
“Field” para que sejam redirecionados para um campo privado da classe.
Pronto. Agora temos tudo que precisamos para aplicar
o pattern. Repare que agora seria o momento em que teríamos que criar métodos
na classe TSampleWrapper que faziam referencias as propriedades e métodos da
classe TSample. Dependendo da estrutura de certas classes, isso levaria horas
para ser feito. Vamos aproveitas a oportunidade e mostrar como isso seria
extremamente rápido usando o pattern Wrapper. Bem, selecione a classe
TSampleWrapper, no “Classes View”, clique na barra de ferramentas na opção
“Patterns” no ModelMaker,

Clique na guia “Structural”, que mantém a
lista de patterns de estrutura, e
clique no botão “Apply Wrapper Pattern”, para aplicar o pattern wrapper na
classe TSampleWrapper. Isso mostrará a seguinte janela:

Nesta janela, devemos informar ao ModelMaker, que
métodos, ou propriedades queremos que a classe selecionada faça wrapper. Repare
que são listados todos os membros da classe TSample, pois o ModelMaker sabia
que se tratava de uma agregação, e portanto seria uma classe candidata ao pattern
wrapper. Escolha a propriedade “SomeValue” e coloque-a na lista de “wrapped
members”. Após isso clique em OK para confirmar a implementação do pattern.
Pronto. A partir daqui não precisamos nos preocupar
com a implementação dos métodos wrappers pois estes já foram criados pelo
ModelMaker. Para ver isso, selecione um dos métodos criados e clique na guia
“Implementation”. Lá você poderá ver a implementação criado pelo ModelMaker, e
a que você provavelmente faria, visto que isso é um pattern!

No quadro acima, na guia
implementation, você pode acrescentar algo ao código gerado criado uma nova
seção no código. Para isso, você deve clicar com o botão direito na guia ao
lado e selecionar “Add Section”. O código que o ModelMaker estrutura é dividido
em seções para a melhor e mais rápida geração de código fonte tanto no
ModelMaker quanto no próprio Delphi.
Para finalizar o exemplo seria interessante
demonstrar duas coisas. Uma é como gerar um diagrama de classes UML do modelo
criado. A outra seria gerar o código fonte Delphi para este modelo. Comecemos
então pela geração do diagrama de classes. Clique na guia “Diagram Editor” na
barra de ferramentas e você verá um diagrama de classes já pronto. Este
diagrama foi criado porque você escolheu a opção “New from default” no menu
File. Vamos remover este diagrama e criar o nosso. Para removê-lo, clique no
botão Pressione “Ctrl + Del”. Isso irá limpar todas as classes do diagrama
atual. Agora, uma por uma, selecione as classes TObject, TPersistent,
TComponent, TSampleWrapper e TSample e arraste até o diagrama. Note que o
relacionamento entre as classes já vai ser criado automaticamente.
|
|
Diagrama
de classes do modelo criado no exemplo. Repare que os símbolos de ligação já
vêm corretamente implementados, a saber, herança e agregação. Talvez no
ModelMaker que você esteja utilizando não apareça os atributos e métodos das
classes. Isso acontece porque o ModelMaker por default não mostra tais
símbolos no diagrama. Você pode configurar isso no menu Options -> Project
Options, guia “Symbol Style” na caixa “Class Symbol member filter”. Lá você
pode marcar o que estará visível no diagrama. |
Veja que dentro do ModelMaker não há limites para se
criar modelos, basta alguns cliques e tudo estará pronto. Agora podemos gerar o
código fonte para o Delphi trabalhar. Note que os passos seguidos neste exemplo
devem servir de inspiração para um projeto real, ou seja, primeiro criamos
nossas classes e diagramas, aplicamos alguns patterns neles e por ultimo,
geramos o código fonte para o Delphi. As mudanças ou alterações feitas no
Delphi devem de preferência se resumir à interface com o usuário e conexão com
banco de dados e/ou servidores de aplicação. Com isso, ganhamos produtividade e
geramos mais tempo hábeis para outras tarefas mais importantes como, realizar
testes e fazer o deployment da aplicação final. Qualquer alteração, inclusive a
codificação e programação deve ser feita dentro do ModelMaker, pois através
dele podemos controlar o projeto por completo.
Para gerar o código fonte para o Delphi, selecione o
menu View e escolha “Units”. Isso mudará o “Classes View” para o “Units View”.
Nessa janela clque com o botão direito do mouse e clique em ”Add New Unit”.
Isso apresentará a seguinte janela:

Configure a janela como a apresentada, e clique em
OK.
A
partir disso, temos a unit que será gerada para o Delphi dentro do ModelMaker.
Agora para finalizar, clique na barra de ferramentas, no botão “UnLock Code
Generation” para desbloquear a geração de código para o Delphi. Este recurso
garante que em determinados momentos do design das classes não seja gerado a
codificação no Delphi. Agora clique no botão “Enable Auto Generation” na barra
de ferramentas que fica dentro do “Units View”. Isso fará com que cada coisa
que seja feita no ModelMaker, reflita na unit criada no Delphi. Para ver o
resultado, vá ao menu Delphi -> Open unit, e o ModelMaker carregará a unit
criada dentro do Code Editor do Delphi. Pronto !!!

A
partir daí, o que resta á apenas criar interfaces para implementar a aplicação.
A qualquer momento você pode alterar a implementação dos métodos dentro do
ModelMaker, e as alterações serão repassadas automaticamente pro Delphi, isto é
se a opção “Enable Auto Generation” estiver ativada.
Conclusão
Vimos neste para onde está caminhando a área de
desenvolvimento de software, e quais tecnologias estão sendo utilizadas. Vimos
também noções básicas de Design Patterns e como aplica-los eficientemente
dentro do Delphi usando o ModelMaker como ferramenta de modelagem. Vale
ressaltar o fato de que um grande projeto de software que utilizar os conceitos
de design patterns e seguindo os princípios da orientação a objetos, terá com
certeza uma considerável vantagem competitiva no mercado de software, pois as
grandes empresas estão procurando cada vez mais profissionais que criem
produtos seguindo estes padrões.
Sobre o Autor |
Ricardo Ferreira É Borland Delphi 7 Advanced Product Certified Developer & Instructor,
e atua como instrutor e Consultor na Argos Tecnologia, em Belém do Pará.
|