|
Usuários |
|
76 Usuários Online
|
|
[Artigos]
[Intermediário] - Arquitetura MVC no Delphi |
Publicado por rboaro : Terça, Junho 25, 2013 - 07:57 GMT-3 (1115 leituras)
5 Comentários Enviar para um amigo Versão para impressão
|
Nada melhor do que desenvolver um sistema utilizando uma boa arquitetura de software, não é? Uma das arquiteturas mais utilizadas por empresas e desenvolvedores de software é o MVC (Model-View-Controller), padrão que fornece organização, padronização e facilidade de manutenção do código. Esse artigo aborda os passos básicos para a elaboração de um projeto arquitetado em MVC no Delphi. Confira!
O objetivo principal deste artigo é mostrar a hierarquia de pastas em um projeto, a alocação das units dentro dessas pastas e a forma como elas se comunicam entre as camadas. Portanto, vou considerar que você já tem conhecimento dessa arquitetura, noções básicas de Orientação a Objetos e experiência com Delphi, ok? Mesmo assim, se você quiser conhecer os conceitos do MVC, leia também este artigo.
Vamos lá! O primeiro passo é criar a pasta raiz do projeto, como “C:\Aplicativo”, por exemplo. Em seguida, criaremos também mais três pastas dentro dela: Model, View e Controller. Essa será a estrutura de subpastas que armazenará nossas units referentes a cada camada. A partir de então, as units criadas deverão ser salvas de acordo com a sua responsabilidade no projeto:
As units das classes de modelagem deverão ser salvas dentro da subpasta Model
Já as units de controle deverão ser salvas dentro da subpasta Controller
Por fim, os formulários deverão ser salvos dentro da subpasta View
O arquivo de projeto (DPR ou DPROJ) deverá ser salvo fora dessas subpastas, ou seja, no diretório raiz da aplicação
Demais arquivos (imagens, arquivos texto, arquivos INI…) opcionalmente podem ser salvos em um diretório próprio, como “Arquivos”
A nomenclatura das units também é importante para facilitar a localização dentro do projeto. Uma boa prática é salvá-las com um prefixo representando o nome da camada seguido do nome de domínio. Por exemplo, se houver o domínio “Cliente”, poderíamos nomear as units da seguinte forma: classeCliente, controleCliente e frmCadastroClientes. Este último recebe o prefixo “frm” por se tratar de um formulário na camada View, embora este prefixo também possa ser utilizado como “form”, “f_” ou até mesmo “visao”.
Alguns desenvolvedores preferem utilizar nomenclaturas em inglês nas units, nomeando-as como classCliente e controllerCliente. Na verdade, o padrão de nomenclatura é relativo de cada desenvolvedor, mas o importante é definir um nome que seja condizente com a camada na qual a unit está alocada.
Ao respeitar essa estrutura de pastas, observe que o Delphi organiza automaticamente a disposição das units dentro de suas respectivas pastas no Project Manager:

Estrutura de pastas exibida no Project Manager do Delphi
A comunicação entre as camadas é realizada por meio da instanciação de objetos das classes. Considerando que temos um domínio de negócio no projeto chamado “Cliente”, poderíamos escrever o bloco de código abaixo para salvar um novo cliente no banco de dados
var
// variáveis das camadas utilizadas na rotina
objetoCliente: TCliente;
objetoControle: TControleCliente;
begin
// instanciação dos objetos
objetoCliente := TCliente.Create; // classe Modelo
objetoControle := TControleCliente.Create; // classe Controle
try
// preenchimento dos dados
objetoCliente.Codigo := StrToIntDef(edtCodigo.Text, 0);
objetoCliente.Nome := Trim(edtNome.Text);
objetoCliente.CPF := edtCPF.Text;
// chamada da rotina para gravação
objetoControle.Salvar(objetoCliente);
finally
// liberação dos objetos da memória
FreeAndNil(objetoCliente);
FreeAndNil(objetoControle);
end;
end;
Atente-se que, ao chamar o método “Salvar” de objControle, os dados do objetoCliente ainda não serão efetivamente gravados no banco de dados. Antes disso, eles passam pela camada Controller, responsável por validar os dados do objeto para evitar que eles sejam transferidos para a camada de acesso a dados (Model) com inconsistências.
Observe que no exemplo acima, um dos atributos da classe “Cliente” é o CPF. Podemos escrever uma função na camada Controller para validar o CPF e, em caso de inválido, abortar a operação de gravação e retornar uma mensagem ao usuário. Essa é uma grande vantagem da arquitetura MVC: durante essa operação de validação não há nenhum acesso à camada de acesso a dados. Na prática, é como se a camada de acesso a dados (Model) ainda não soubesse que o usuário está incluindo um novo cliente. Ela só irá receber o objeto quando estiver validado e consistente. Interessante, não é?
A camada Controle, por sua vez, terá o seguinte código no método “Salvar”:
procedure TControleCliente.Salvar(const objetoCliente: TCliente);
begin
// aqui devem ser escritas as funções de validação
objetoCliente.Salvar(objetoCliente);
end;
Veja que utilizamos o próprio objeto passado por parâmetro para chamar a função “Salvar” da camada Model. Aproveitando a oportunidade, vale ressaltar uma observação importante: muitos desenvolvedores preferem estender a camada Model na arquitetura MVC e criar uma camada exclusiva de acesso a dados, chamada DAO (Data Access Object). Eu confesso que sou um desses desenvolvedores, rsrs.
Ao criar a camada DAO, é possível separar a modelagem de dados (atributos da classe) e os métodos de acesso a dados (Salvar, Alterar, Excluir, etc) em units diferentes. Lógico, neste caso, é necessário criar mais uma subpasta no diretório da aplicação chamada DAO. A introdução dessa camada também irá interferir na camada Controller. Por exemplo, utilizando o código anterior como comparação, a chamada do método “Salvar” é alterada para a seguinte forma:
procedure TControleCliente.Salvar(const objetoCliente: TCliente);
var
objetoDAO: TDAOCliente;
begin
objetoDAO := TDAOCliente.Create;
try
// aqui devem ser escritas as funções de validação
objetoDAO.Salvar(objetoCliente);
finally
FreeAndNil(objetoDAO);
end;
end;
Embora o código fique ligeiramente maior, a compreensão não fica comprometida. A diferença é que, ao invés de chamar a camada Model para persistir os dados, chamamos a camada DAO.
Antes de finalizar o artigo, gostaria de esclarecer uma dúvida comum de desenvolvedores que começam a trabalhar com Orientação a Objetos no Delphi, principalmente desenvolvedores que vieram de outras linguagens, como C++, C# e Java. Essa dúvida é relacionada aos getters e setters, que são métodos de leitura e escrita dos atributos de uma classe.
No Delphi não há getters e setters, mas introduz um modo diferente de manipular os atributos: através de propriedades (property). Em apenas uma linha, é possível declarar a propriedade e suas variáveis de leitura e escrita, como o exemplo abaixo:
type
TCliente = class
private
FNome: string;
public
property Nome: string read FNome write FNome;
end;
Ou então, caso necessário, declarar métodos para leitura e escrita:
type
TCliente = class
private
FNome: string;
procedure SetNome(Valor: string);
function GetNome: string;
public
property Nome: string read GetNome write SetNome;
end;
Legal, não é?
O exemplo contido neste artigo (com alguns incrementos) pode ser baixado neste link.
Pessoal, eu vou ficando por aqui e agradeço a vocês pela visita no SubRotina!
Qualquer dúvida ou dificuldade no desenvolvimento de um projeto em MVC, entre em contato!
Abraços!
|
|
Comentários | |
| | Comentários pertencem aos seus respectivos autores. Não somos responsáveis pelo seus conteúdos. |
por: ronneipeterson (ronneipeterson@gmail.com)
: Jun 25, 2013 - 10:44 (Informações sobre o membro | Enviar uma mensagem)
http://www.invictos.com.br
|
Interessante a idéia, a uns anos atrás escrevi dois artigos na revista Active Delphi parecido com essa idéia, os títulos era "Acesso a Banco de Dados Relacional com uso da Orientação a Objeto", o padrão é parecido com o MVC, porém não existe a camada de validação, ela fica junto com a camada de acesso, porém depois de muitos anos usando este conceito passei a ter problemas nas chaves estrangeiras, pois para cada chave estrangeira eu instanciava a classe daquela chave, isso em algumas situações criou instâncias em cascatas, com centenas de objetos, trazendo lentidão e "out of memory" em alguns casos. Gostaria de saber como você tem tratado a situação de chave estrangeira, tem instanciado classes? Ou tem declarado a propriedade apenas como uma string e recebendo apenas a chave primária?
Abraço e Sucesso!
|
por: Batera (andreinfo@hotmail.com) : Jun 25, 2013 - 02:05 (Informações sobre o membro | Enviar uma mensagem) http://www.subrotina.com.br | | Boa observação! Nos últimos projetos, tenho trabalhado com variáveis integer (ou smallint, dependendo da entidade) nesse tipo de relacionamento ao invés de agregar uma classe dentro da outra. Essa prática trouxe simplicidade para a minha solução, principalmente na manipulação das instâncias dos objetos. Há desenvolvedores que preferem a prática de compor classes, por exemplo, em relacionamentos master/detail, e também concordo com eles. Mesmo assim, acredito que a melhor prática a ser aplicada depende das particularidades do desenvolvedor e do tipo de projeto em pauta. Obrigado pelo comentário. | [ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
|
|
Edição 112 |
|
|
50 Programas Fontes |
|
|
Produtos |
|
|