- Comportamentais -Interpreter, Template Method, Chain of Responsability, Command, Iterator, Mediator, Memento, Observer, State, Strategy, Visitor. Hoje existe uma gama enorme de material sobre Design Patterns, porm mais comum encontrar este material para Java e .Net, no muito comum vermos exemplos de aplicaes prticas de Design Patterns em aplicaes Delphi. exatamente isso que vamos tratar aqui, demonstrar de forma prtica, como aplicar Design Patterns em problemas reais que podemos enfrentar diariamente em nossos projetos. Primeiramente ns explicaremos os Patterns que usaremos e depois desenvolveremos uma aplicao simples e ao longo dela iremos implementar os Patterns pertinentes a cada tipo de problema. Aqui usaremos o Singleton para garantirmos apenas uma instncia de nosso objeto Aplicacao e Conexao, o DAO para abstrair o acesso a dados do nosso sistema e a Factory para abstrair e isolar a criaes de nossas DAOs.
Singleton
De acordo com o GOF, o objetivo do Singleton garantir que uma classe possua apenas uma instncia dentro da sua aplicao e que esta instncia possua um ponto de acesso global a todo o sistema. O Singleton precisa garantir que no teremos duas instncias de um determinado objeto em nosso sistema. Como podemos notar na Figura 1, a implementao tradicional do Singleton prev que haja um atributo privado e esttico do tipo da classe, aqui no exemplo chamado de FInstance. Esse atributo onde ficar a nica instncia de nosso objeto.
Figura 1. O padro Singleton Observe tambm que o construtor est privado, isso necessrio para que no seja possvel criar instncias deste objeto em qualquer lugar do sistema. O mtodo GetInstance o nosso ponto de acesso global nossa instncia. Veja na Listagem 1 a implementao em Delphi desta classe. Listagem 1. Implementao padro do Singleton
unit UMySingleton; interface type TMySingleton = class private
class var FInstance:TMySingleton; constructor create(); public class function GetInstance(): TMySingleton; end; implementation { TMySingleton } constructor TMySingleton.create; begin inherited; end; class function TMySingleton. GetInstance: TMySingleton; begin //Verifica se a instncia j foi criada //caso no tenha sido, cria a mesma usando o construtor privado if (not Assigned(FInstance)) then FInstance := create; //retorna a instncia do Singleton result := FInstance; end; end.
Factory
Este Pattern centraliza a criao de objetos concretos, que possuam uma interface em comum, em uma classe. Esta classe quem decide qual instncia concreta deve ser criada e retornada para o client. No existe uma regra para a condio que servir como base para que a Factory tome a deciso de qual classe criar, porm existem dois modelos que so bastante usados que so por parmetro e por deteco.
Nota: Quando estamos falando em Design Patterns, chamamos de client a classe que consome um determinado servio de outra. Quando por parmetro, a Factory possui um mtodo para criao e retorno do objeto concreto e este mtodo recebe como parmetro alguma informao que possibilite a Factory identificar qual objeto deve ser criado. Quando por deteco, o mtodo da Factory no recebe nenhum parmetro, porm decide qual objeto criar baseado na deteco de alguma informao ou configurao do sistema, como por exemplo, atravs de uma configurao em um arquivo INI ou XML. Observe a arquitetura deste Pattern na Figura 3, para criao de objetos que representam produtos. A Listagem 3 mostra o cdigo Delphi deste Pattern. Onde: Client quem vai solicitar uma instncia do AbstractProduct; AbstractProduct - Abstrao do objeto que ser instanciado e retornado pela Factory. Pode ser uma classe base ou uma interface; ConcreteProductA e B - Duas implementaes diferentes de AbstractProduct. Sero os retornos da Factory.
Na Listagem 3 ns usamos a verificao por parmetro para decidir qual objeto concreto retornar. Com isso abstramos de nosso client a implementao concreta de nosso produto. O nosso client passa a trabalhar apenas com nosso produto abstrato, assim ns diminumos o acoplamento e deixamos nosso projeto mais flexvel a mudanas.
um de nossos modelos. Estas DAOs implementaro a interface IDAO e usaro os servios de conexo que, no nosso caso, estaro encapsulados na classe TConexaoBD, que por sua vez ter toda a configurao para conexo com o banco, alm de uma instncia de um TSQLConnection que ser fornecida s DAOs. Na parte de baixo do diagrama temos nossos modelos herdando de um modelo base. Este modelo base ter um identificador e um State, que o que usaremos para saber qual mtodo da persistncia deveremos executar (Insert, Update, Delete). Teremos uma Factory para construir nossas DAOs. Observe que dessa maneira os formulrios desconhecem completamente a implementao da DAO que lhes atender, eles conhecem apenas a DaoFactory e ela que vai decidir qual DAO retornar de acordo com o modelo passado como parmetro. Temos ainda uma classe encapsulando aspectos da aplicao, como por exemplo, a verso, a data e hora de login e o usurio logado.
Construindo os modelos
Na Listagem 4 temos a implementao de um tipo enumerado chamado TStateDomain, que servir para identificar qual o estado do nosso modelo e assim decidirmos qual mtodo de persistncia usar. Alm deste tipo enumerado temos nosso modelo base, ele bem simples, a princpio temos apenas um ID que ser o identificador do nosso objeto e um StateDomain para nos indicar o estado do objeto. Na Listagem 5 temos a implementao de nossos modelos Cliente e Usurio, ambos herdam de nosso modelo base TBaseDomain. Note que como eles herdam de TBaseDomain, no precisamos definir as propriedades Id e State. So estes domnios que
sero passados para as DAOs para serem persistidos. Note que o cdigo extremamente simples, apenas define a classe, seus atributos e mtodos get / set.
FHoraCadastro := Value; end; procedure TCliente.SetIdade(const Value: Integer); begin FIdade := Value; end; procedure TCliente.SetNome(const Value: String); begin FNome := Value; end; procedure TCliente.SetUsuarioCadastro (const Value: TUsuario); begin FUsuarioCadastro := Value; end; end. unit UUsuario; interface uses UBaseDomain; type TUsuario = class(TBaseDomain) private FSenha: String; FLogin: String; FNome: String; procedure SetLogin(const Value: String); procedure SetNome(const Value: String); procedure SetSenha(const Value: String); published property Login:String read FLogin write SetLogin; property Senha:String read FSenha write SetSenha; property Nome:String read FNome write SetNome; end; implementation { TUsuario } procedure TUsuario.SetLogin(const Value: String); begin FLogin := Value; end; procedure TUsuario.SetNome(const Value: String); begin FNome := Value; end;
Nesta classe aplicaremos o Singleton para evitar que, por algum erro de implementao, tenhamos duas instncias de conexo em nossa aplicao (Listagem 6).
Listagem 6. Implementao da classe de conexo unit UConexaoBD; interface uses SqlExpr; type TConexaoBD = class private FConexaoBD: TSQLConnection; FDataBaseFile: String; FPassword: String; FUserName: String; //Varivel esttica para armazenar a //nica instncia de nossa classe class var FInstance:TConexaoBD; procedure SetConexaoBD(const Value: TSQLConnection); procedure SetDataBaseFile(const Value: String); procedure SetPassword(const Value: String); procedure SetUserName(const Value: String); procedure LoadDefaultConfig(); //Construtor privado para que possamos criar //o objeto apenas de dentro da prpria classe constructor create(); published property ConexaoBD:TSQLConnection read FConexaoBD write SetConexaoBD; property DataBaseFile:String read FDataBaseFile write SetDataBaseFile; property UserName:String read FUserName write SetUserName; property Password:String read FPassword write SetPassword; public //Mtodo esttico para retornar nosso //objeto de conexo class function GetInstance():TConexaoBD; procedure LoadConfig(); end;
implementation { TConexaoBD }
constructor TConexaoBD.create; begin inherited; //Criamos nossa connection que ser //fornecida para as DAOs ConexaoBD := TSQLConnection.Create(nil); //Carregamos as configuraes default da conexo LoadDefaultConfig; //Carregamos as configuraes de conexo LoadConfig; end; class function TConexaoBD.GetInstance: TConexaoBD; begin if(not Assigned(FInstance))then FInstance := create; result := FInstance; end; procedure TConexaoBD.LoadConfig; begin //Verificamos se o connection est criado // para evitarmos um AccessViolation if(Assigned(ConexaoBD))then begin if(not ConexaoBD.Connected)then begin //Definimos o nome da conexo para carregar as //informaes padres de conexo ConexaoBD.ConnectionName := FBConnection; ConexaoBD.DriverName := Firebird; //LoadParamsFromIniFile carregar os //parmetros padres de //conexo com base no ConnectionName ConexaoBD.LoadParamsFromIniFile(); //Definimos nossas configuraes de usurio, //senha e caminho do BD ConexaoBD.Params[ConexaoBD.Params. IndexOfName(Password)] := Password=+Password; ConexaoBD.Params[ConexaoBD.Params. IndexOfName(User_Name)] := User_Name=+UserName; ConexaoBD.Params[ConexaoBD.Params. IndexOfName(Database)] := Database=+DataBaseFile; ConexaoBD.LoginPrompt := false; //Abrimos a conexo ConexaoBD.Connected := true; end; end; end; procedure TConexaoBD.LoadDefaultConfig; begin
DataBaseFile := localhost:D:\Artigos\ ClubeDelphi\Singleton\bd\ExDP.FDB; UserName := SYSDBA; Password := masterkey; end; procedure TConexaoBD.SetConexaoBD (const Value: TSQLConnection); begin FConexaoBD := Value; end; procedure TConexaoBD.SetDataBaseFile (const Value: String); begin FDataBaseFile := Value; end; procedure TConexaoBD.SetPassword (const Value: String); begin FPassword := Value; end; procedure TConexaoBD.SetUserName (const Value: String); begin FUserName := Value; end; end.
Implementando as DAOs
Vamos fazer mais uma analogia ao desenvolvimento RAD. Quando desenvolvemos de forma RAD, normalmente colocamos os DataSets no formulrio / DM e j inserimos ali mesmo o cdigo SQL para manipulao dos dados. Dessa forma fica tudo junto, misturado, impossibilitando o reuso. Se voc faz um cadastro de clientes dessa forma, no conseguir reutilizar o cdigo nele presente. Imagine que em outra ocasio voc precise fazer a importao de clientes de outra base de dados, fatalmente ter que reescrever o cdigo de insero de clientes. A implementao da DAO ajuda neste aspecto, pois como o cdigo SQL ficar isolado em uma classe, conseguimos reutilizar o mesmo em diferentes ocasies, alm de facilitar a manuteno. Na Listagem 7 temos a definio da nossa interface IDAO, que possui todo o comportamento comum a todas as DAOs da nossa aplicao. A implementao de uma interface fundamental para que possamos implementar nossa Factory, como veremos mais adiante. Listagem 7. Definio da interface IDAO
unit UIDao;
interface uses UBaseDomain, Generics.Collections; type IDao = interface procedure Insert(pDomain:TBaseDomain); procedure Update(pDomain:TBaseDomain); procedure Delete(pDomain:TBaseDomain); //Retorna um modelo com base em um ID function GetById(pIdDomain:Integer): TBaseDomain; //Retorna um modelo com base em campo e valor Function GetByField(Field:String; Value:String):TBaseDomain; //Retorna uma lista com todos os modelos function GetAll():TList<TBaseDomain>; End; implementation end.
Na Listagem 8 podemos ver a implementao dos mtodos de persistncia de cliente. Note que em todos os casos ns seguimos uma ordem onde primeiramente criamos a query, recuperamos a conexo com o BD, montamos a SQL, passamos os parmetros e enfim executamos a SQL. Aps isso, mudamos o estado do objeto para sdBrowse, pois a operao fora realizada. Note que necessrio realizarmos um Cast do parmetro que do tipo TBaseDomain para acessarmos os valores de nosso modelo cliente. Observe tambm que usamos a nossa TConexaoBD para recuperar a conexo com o BD e associar nossa query. No mtodo inserir temos uma particularidade que a persistncia do objeto associado, que o usurio que cadastrou o cliente em questo, ento pegamos o ID do mesmo para persistir no BD.
Function GetById(pIdDomain:Integer): TBaseDomain; Function GetByField(Field:String;Value: String):TBaseDomain; function GetAll: TList<TBaseDomain>; End; implementation uses UDaoFactory,UUsuario; { TClienteDao } procedure TClienteDao.Insert(pCliente: TBaseDomain); var Qry:TSQLQuery; begin Qry := TSQLQuery.Create(nil); Qry.SQLConnection := TConexaoBD. GetInstance.ConexaoBD; Qry.SQL.Add(INSERT INTO CLIENTE(NOME,IDADE, BAIRRO,ID_USUARIO) VALUES); Qry.SQL.Add((:pNOME,:pIDADE,:pBAIRRO, :pID_USUARIO)); Qry.ParamByName(pNOME).AsString := TCliente(pCliente).Nome; Qry.ParamByName(pIDADE).AsInteger := TCliente(pCliente).Idade; Qry.ParamByName(pBAIRRO).AsString := TCliente(pCliente).Bairro; Qry.ParamByName(pID_USUARIO).AsInteger := TCliente(pCliente).UsuarioCadastro.Id; Qry.ExecSQL(); pCliente.State := sdBrowse; end; procedure TClienteDao.Update(pCliente: TBaseDomain); var Qry:TSQLQuery; begin Qry := TSQLQuery.Create(nil); Qry.SQLConnection := TConexaoBD. GetInstance.ConexaoBD; Qry.SQL.Add(UPDATE CLIENTE SET BAIRRO =: pBAIRRO,IDADE = :pIDADE,NOME = :pNOME); Qry.SQL.Add( WHERE ID = :pID); Qry.ParamByName(pNOME).AsString := TCliente(pCliente).Nome; Qry.ParamByName(pIDADE).AsInteger := TCliente(pCliente).Idade; Qry.ParamByName(pBAIRRO).AsString := TCliente(pCliente).Bairro; Qry.ParamByName(pID).AsInteger := TCliente(pCliente).Id; Qry.ExecSQL(); pCliente.State := sdBrowse; end;
procedure TClienteDao.Delete(pCliente: TBaseDomain); var Qry:TSQLQuery; begin Qry := TSQLQuery.Create(nil); Qry.SQLConnection := TConexaoBD. GetInstance.ConexaoBD; Qry.SQL.Add(DELETE FROM CLIENTE ); Qry.SQL.Add( WHERE ID = :pID); Qry.ParamByName(pID).AsInteger := TCliente(pCliente).Id; Qry.ExecSQL(); pCliente.State := sdBrowse; end;
Na Listagem 9 temos a implementao de nossos mtodos de recuperao de dados, onde seguimos o mesmo padro, criando a query, recuperando a conexo, montando a SQL, passando os parmetros, executando a SQL e por fim populamos um objeto Cliente para ser retornado. Observe como populamos a propriedade UsuarioCadastro, neste caso ns solicitamos DaoFactory uma nova DAO de usurio e ento executamos o mtodo GetByID passando o ID recuperado em nossa consulta. O GetALL tem uma particularidade que o retorno de uma lista com todos os clientes cadastrados. Para isso fazemos a consulta no BD e ento percorremos todo o resultado da query, instanciando um cliente para cada registro da consulta e adicionando este cliente nossa lista para ser retornada pelo mtodo. Em todos os casos ns reutilizamos o mtodo GetById da UsuarioDao, algo que provavelmente no seria possvel se no tivssemos isolado a parte de acesso a dados em classes DAOs. Listagem 9. Mtodos Get da DaoCliente
function TClienteDao.GetAll: TList<TBaseDomain>; var Qry:TSQLQuery; Lista:TList<TBaseDomain>; Cliente:TCliente; begin Qry := TSQLQuery.Create(nil); Qry.SQLConnection := TConexaoBD. GetInstance.ConexaoBD; Qry.SQL.Add(SELECT ID,NOME,IDADE,BAIRRO, ID_USUARIO FROM CLIENTE ); Qry.Open(); lista := TList<TBaseDomain>.Create(); while not(Qry.Eof) do begin Cliente := TCliente.Create; Cliente.Id := Qry.FieldByName(ID). AsInteger; Cliente.Nome := Qry.FieldByName(NOME). AsString; Cliente.Idade := Qry.FieldByName(IDADE). AsInteger;
Cliente.Bairro := Qry.FieldByName(BAIRRO). AsString; Cliente.UsuarioCadastro := TUsuario(TDaoFactory. GetDao(TUsuario.Create()).GetById(Qry. FieldByName(ID_USUARIO).AsInteger)); Lista.Add(Cliente); Qry.Next; end; Result := lista; end; function TClienteDao.GetByField(Field, Value: String): TBaseDomain; var Cliente:TCliente; Qry:TSQLQuery; begin Cliente := TCliente.Create; Qry := TSQLQuery.Create(nil); Qry.SQLConnection := TConexaoBD. GetInstance.ConexaoBD; Qry.SQL.Add(SELECT SELECT ID,NOME,IDADE, BAIRRO,ID_USUARIO FROM CLIENTE ); Qry.SQL.Add( WHERE + Field + = + quotedStr(Value)); Qry.Open(); Cliente.Id := Qry.FieldByName(ID).AsInteger; Cliente.Nome := Qry.FieldByName(NOME).AsString; Cliente.Idade := Qry.FieldByName(IDADE).AsInteger; Cliente.Bairro := Qry.FieldByName(BAIRRO).AsString; Cliente.UsuarioCadastro := TUsuario(TDaoFactory.GetDao (TUsuario.Create()).GetById(Qry. FieldByName(ID_USUARIO).AsInteger)); result := Cliente; end; function TClienteDao.GetById(pIdDomain: Integer): TBaseDomain; var Qry:TSQLQuery; Cliente:TCliente; begin Qry := TSQLQuery.Create(nil); Qry.SQLConnection := TConexaoBD. GetInstance.ConexaoBD; Qry.SQL.Add(SELECT ID,NOME,IDADE,BAIRRO, ID_USUARIO FROM CLIENTE ); Qry.SQL.Add( WHERE ID = :pID); Qry.ParamByName(pID).AsInteger := pIdDomain; Qry.Open(); Cliente := TCliente.Create; Cliente.Id := Qry.FieldByName(ID).AsInteger; Cliente.Nome := Qry.FieldByName(NOME).AsString; Cliente.Idade := Qry.FieldByName(IDADE).AsInteger; Cliente.Bairro := Qry.FieldByName(BAIRRO).AsString; Cliente.UsuarioCadastro :=
Enquanto o mtodo GetALL retorna a lista de um determinado objeto usando Generics, os mtodos GetByID e GetByField retornam apenas um objeto dependendo do parmetro passado. O primeiro retorna o objeto de acordo com o ID informado, j o segundo permite uma consulta por qualquer atributo do objeto, basta informar o nome do atributo e seu valor. Nota: O mtodo GetAll utiliza um novo recurso disponvel desde o Delphi 2009 em Win32 chamado Generics. Com Generics podemos abstrair um tipo de dado e defini-lo em tempo de execuo. Isso flexibiliza o cdigo e evita o uso excessivo de Type Casting. Na Listagem 10 temos a implementao de nossa Factory. No mtodo GetDao ns recebemos um modelo como parmetro e verificamos com o operador IS qual o tipo do modelo para ento criarmos a DAO correta. Fechamos aqui a implementao de nossos modelos e nossas DAOs, ns no falaremos da DAOUsuario pois segue o mesmo padro da DAOCliente. Listagem 10. DaoFactory
unit UDaoFactory; interface uses UBaseDomain, UIDao, UCliente, UUsuario, UClienteDao, UUsuarioDao; type TDaoFactory = class class function GetDao(pDomain: TBaseDomain):IDao; end; implementation { TDaoFactory<T> } class function TDaoFactory.GetDao(pDomain: TBaseDomain): IDao; var ConcreteDao:IDao; begin if(pDomain is TCliente)then ConcreteDao := TCLienteDao.create() else if(pDomain is TUsuario)then ConcreteDao := TUsuarioDao.create();
inherited; end; class function TAplicacao.GetInstance: TAplicacao; begin if not assigned(FInstance)then FInstance := create(); result := FInstance; end; procedure TAplicacao.SetDataHoraLogin (const Value: TDateTime); begin FDataHoraLogin := Value; end; procedure TAplicacao.SetUsuarioLogado (const Value: TUsuario); begin FUsuarioLogado := Value; end; procedure TAplicacao.SetVersao (const Value: String); begin FVersao := Value; end; end.
Construindo os formulrios
J falamos de todas as nossas classes de domnio, persistncia e infraestrutura, agora vamos implementao de nossos formulrios, a comear pelo formulrio de login (Figura 5).
Figura 5. Tela de Login Na Listagem 12 temosa validao de usurios. Primeiro ns solicitamos uma DAO para usurio. Depois chamamos o mtodo GetByField passando o campo que desejamos filtrar, que neste caso o LOGIN e o respectivo valor digitado no edit login. Este mtodo nos retornar o usurio que possui este login, ento na linha de baixo ns verificamos se a instncia de usurio no est criada ou se a senha diferente, isto porque se o login for invlido, o usurio ser igual nil. Se uma destas condies for verdadeira, ns exibimos uma mensagem ao usurio, caso contrrio seguimos a execuo do programa criando o formulrio principal e atribuindo o usurio retornado da DAO ao UsuarioLogado de nossa aplicao. Em nosso formulrio de cadastro de clientes, temos uma ComboBox que exibir todos os clientes cadastrados e medida que um cliente for selecionado, ir carregar seus dados no formulrio permitindo a alterao ou excluso do mesmo. A Figura 6 mostra uma sugesto de tela. Neste formulrio ns temos declarado na sesso private trs atributos, conforme mostrado a seguir: private { Private declarations } Cliente:TCliente; Dao:IDao; ListaClientes:TList<TCliente>;
Na Listagem 13 ns limpamos o contedo de nossa ComboBox e ento chamamos o mtodo GetAll de nossa DAO que retornar uma lista com todos os clientes cadastrados para nossa ListaClientes, depois disso ns percorremos esta lista adicionando os itens em nosso ComboBox. No esquea de declarar a procedure LoadListAtCombo na seo private do formulrio. A seguir vemos o cdigo do FormShow onde criamos a DAO de cliente e
em seguida carregamos a lista de clientes cadastrados em nossa ComboBox: procedure TFrmCadCliente.FormShow(Sender: TObject); begin Dao := TDaoFactory.GetDao(TCliente.Create); LoadListAtCombo(); end; Listagem 13. Carga dos clientes na ComboBox
procedure TFrmCadCliente.LoadListAtCombo; var c:TCliente; begin CbbClientes.Items.Clear; ListaClientes := TList<TCliente> (Dao.GetAll()); for c in ListaClientes do begin CbbClientes.AddItem(c.nome,c); end; end;
Na Listagem 14 temos os mtodos responsveis por passar os valores do modelo selecionado para os controles da tela (ModelToView) e vice-versa, da tela para o modelo a ser persistido (ViewToModel). Listagem 14. Mtodos para trocar dados entre tela e domnio
procedure TFrmCadCliente.ModelToView (pCliente: TCliente); begin edtNome.Text := pCliente.Nome; EdtIdade.Text := IntToStr (pCliente.Idade); EdtBairro.Text := pCliente.Bairro; EdtUsuarioCadastro.Text := pCliente.UsuarioCadastro.Nome; end; procedure TFrmCadCliente.ViewToModel (pCliente: TCliente); begin pCliente.Nome := edtNome.Text; pCliente.Idade := StrToInt (EdtIdade.Text); pCliente.Bairro := EdtBairro.Text; pCliente.DiaCadastro := DateOf(now); pCliente.HoraCadastro := TimeOf(now); end;
Na Listagem 15 temos o cdigo dos botes Novo, Editar e Excluir que seguem o mesmo padro, apenas alteram o estado do modelo e o estado dos botes da tela. No caso
do novo temos ainda a instanciao de um novo cliente, o que no necessrio no caso do editar, pois o cliente j est carregado. No caso da excluso ns simplesmente executamos o Delete da DAO passando o cliente como parmetro, aps isso executamos o LoadListAtCombo apenas para atualizarmos a nossa lista. Listagem 15. Cdigo dos botes Novo, Editar e Excluir
procedure TFrmCadCliente.BtnNovoClick (Sender: TObject); begin Cliente := TCliente.Create; Cliente.State := sdNew; BtnGravar.Enabled := true; BtnCancelar.Enabled := True; BtnExcluir.Enabled := false; BtnEditar.Enabled := false; BtnNovo.Enabled := false; ClearControls; end; procedure TFrmCadCliente.BtnEditarClick (Sender: TObject); begin Cliente.State := sdEdit; BtnGravar.Enabled := true; BtnCancelar.Enabled := True; BtnExcluir.Enabled := false; BtnEditar.Enabled := false; BtnNovo.Enabled := false; end; procedure TFrmCadCliente.BtnExcluirClick (Sender: TObject); begin Dao.Delete(Cliente); LoadListAtCombo(); ShowMessage(Cliente excludo com sucesso.); end;
Ao selecionarmos um cliente na ComboBox, exibiremos seus dados na tela, para isso temos o cdigo a seguir implementado no evento OnSelect da ComboBox: procedure TFrmCadCliente.CbbClientesSelect(Sender: TObject); begin Cliente := ListaClientes[CbbClientes.ItemIndex]; ModelToView(Cliente); end; Na Listagem 16 temos o mtodo de gravao, nele executamos nosso mtodo para passar os valores da tela para o nosso modelo e ento verificamos o estado do mesmo para ento executarmos o Insert ou Update. No caso de uma incluso, ns alimentamos ainda o
UsuarioCadastro do cliente, usando o Singleton de TAplicao para pegarmos a instncia de TAplicacao e em seguida o usurio logado, que alimentamos na tela de login. Listagem 16. Cdigo do boto Gravar
procedure TFrmCadCliente. BtnGravarClick(Sender: TObject); begin ViewToModel(Cliente); if(Cliente.State = sdNew)then begin Cliente.UsuarioCadastro := TAplicacao.GetInstance. UsuarioLogado; Dao.Insert(cliente); end else if(Cliente.State = sdEdit)then Dao.Update(cliente); BtnGravar.Enabled := false; BtnCancelar.Enabled := false; BtnExcluir.Enabled := true; BtnEditar.Enabled := true; BtnNovo.Enabled := true; ShowMessage(Cliente gravado com sucesso.); end;
Concluso
Com isso fechamos nossa pequena aplicao de cadastro de clientes, porm com a aplicao de trs patterns bem comuns, o Singleton, Factory e DAO, para nos ajudar a resolver necessidades comuns do nosso dia-a-dia. Mesmo neste simples exemplo podemos notar alguns grandes benefcios do uso destes Patterns, a comear pelo Singleton, que nos d a garantia que s teremos uma instncia de Conexo e Aplicao em nosso sistema. O uso de DAO isolou todo o cdigo de acesso a dados nos permitindo desacoplar mais nossa aplicao, aumentando sua coeso e tambm a reutilizar mais cdigo. Finalizando com a Factory, que centralizou a criao das DAOs em um nico lugar e que fez as classes consumidoras das DAOs, incluindo os formulrios, desconhecerem completamente a implementao das DAOs concretas, fazendo-as trabalhar apenas com a interface IDAO.