Olá, leitores! Quanto tempo, hein?
Depois de alguns compromissos e problemas de saúde, finalmente retorno aos meus trabalhos aqui no blog.
Conforme prometido, hoje inicio uma nova série sobre Design Patterns GRASP! Embora poucos conhecidos, eles apresentam boas práticas de codificação que contribuem para uma arquitetura desacoplada. Vamos lá?
Introdução
Se você considera que só existem os Design Patterns do GoF (Gang of Four), está enganado! Há vários outros padrões de projeto distribuídos em diversas literaturas. Um destes conjuntos de padrões é chamado de GRASP – acrônimo de General Responsibility Assignment Software Patterns, ou, em tradução livre: “Padrões de Software de Atribuição de Responsabilidade Geral”.
Os nove padrões de projeto do GRASP estão relacionados basicamente com as responsabilidades que as classes possuem em um projeto. Neste momento acredito que você deve ter lembrado do SRP, não é? 🙂
No decorrer dessa série, você notará que alguns deles se assemelham aos padrões do GoF, e isso é bom! Essa equivalência entre os padrões alimenta a nossa sensibilidade técnica.
Controller
Pois bem, pessoal, o primeiro destes padrões é o Controller, que traz um conceito bem fácil: seu propósito é delegar eventos e regras de negócio para uma classe não-visual. Em um simples cadastro de clientes, por exemplo, haveriam duas classes: uma responsável pela apresentação visual (TCadastroClientes
) e outra responsável por controlar as regras de negócio (TCadastroClientesController
). Observe que essa separação já é algo relativamente comum nas arquiteturas atuais.
Isso nos lembra… o padrão MVC! 🙂
O “C” refere-se ao Controller, que faz o intermédio entre as camadas View e Model. Lembre-se que, neste padrão de arquitetura, a responsabilidade de validações, transporte de dados e parte das regras de negócio é incumbida ao Controller. Uma das grandes vantagens é o baixo acoplamento entre classes, já que cada uma recebe apenas uma responsabilidade. Logo, podemos dizer que este padrão de projeto está “dentro” do MVC!
Show me the code!
Essa é uma nova seção do blog, leitores. Trata-se de frase que foi citada por Linus Torvalds em 2000 e achei conveniente utilizá-la nas partes práticas dos artigos.
Para exemplificar o uso do Controller, codificaremos o código da inclusão de um novo registro em um cadastro de produtos. Antes disso, veja como seria o código sem a aplicação do padrão de projeto:
1 2 3 4 5 6 |
DataSet.Append; DataSet.FieldByName('Codigo').AsInteger := StrToInt(EditCodigoProduto.Text); DataSet.FieldByName('Descricao').AsString := EditDescricao.Text;; DataSet.FieldByName('Preco').AsFloat := StrToFloat(EditValor.Text); DataSet.Post; DataSet.ApplyUpdates(0); |
O código parece estar correto, concorda?
Realmente não há nada de errado, porém, considerando também os métodos de exclusão, alteração, consulta, validações e manipulação dos componentes visuais, a classe provavelmente ficará sobrecarregada de responsabilidades. No caso de formulários no Delphi, já podemos contar três responsabilidades: apresentação visual, validações e persistência.
A ideia do Controller é encapsular estes métodos em uma classe isolada, de forma que a camada View (parte visual) tome conta apenas das regras de exibição ao usuário. Para isso, portanto, teríamos uma nova classe:
1 2 3 4 5 |
TProdutoController = class public procedure IncluirProduto(const ACodigo: integer; const ADescricao: string; const APreco: real); end; |
No formulário (View), basta criar um objeto Controller e invocar o método de inclusão do produto:
1 2 3 4 5 6 7 8 9 10 11 |
var Controller: TProdutoController; begin Controller := TProdutoController.Create; try Controller.IncluirProduto( StrToInt(EditCodigo.Text), EditDescricao.Text, StrToFloat(EditPreco.Text)); finally Controller.Free; end; end; |
Opcionalmente, podemos criar uma classe DTO (Data Transfer Object) para enviar apenas um parâmetro para a função ao invés de três. Recomendo essa opção!
Ao delegar tais responsabilidades para o Controller, a camada View fica encarregada apenas das regras visuais, como atualização de textos, preenchimento de listas, exibição de mensagens e controle do estado de componentes.
Componentes visuais e regras de negócio
Para controlar componentes visuais, use o Controller também! Veja um exemplo abaixo:
1 2 3 4 5 6 7 8 9 10 |
var Controller: TProdutoController; begin Controller := TProdutoController.Create; try ButtonFoto.Enabled := Controller.VerificarProdutoPossuiFoto; finally Controller.Free; end; end; |
Simples, né? Mas… há algo que nos incomoda: é preciso instanciar e liberar o Controller a todo momento, tornando o código dos métodos muito repetitivo.
Bom, já que o Controller normalmente é utilizado em quase todos os métodos da camada View, é mais viável, obviamente, instanciá-lo como uma variável de classe:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
type Form1 = class(TForm) private { Private declarations } FController: TProdutoController; { ... } procedure TForm.FormCreate(Sender: TObject); begin FController := TProdutoController.Create; end; procedure TForm.FormDestroy(Sender: TObject); begin FController.Free; end; |
That’s it!
Agora que você já conhece o Controller, tenho certeza que, em algum momento, já codificou algo parecido, ou seja, uma classe que encapsula o controle de eventos e regras de negócio de forma isolada, não visual, e exclusiva. Mesmo que você não tenha adicionado o sufixo “Controller” (que, claro, não é uma regra), essa classe é um Controller!
Na verdade, como mencionei no início do artigo, esse tipo de codificação já é bastante recomendado.
Fico por aqui, e encontro vocês no segundo artigo dessa série!
Bom dia André,
Estou utilizando uma Classe TControllerFormsGeral para fazer a chamada dos formulários em qualquer ponto do sistema e onde abro um formeCliente e formeCidade ao mesmo tempo como se você MDI. Em cada formulário coloquei o initialization e suas respectiva classes. Exemplo:
O problema é na unit uControllerFormsGeral neste código:
Tem como testar o código aliás estou usando o Delphi 7. No Delphi 10.3.3 uso TObjectClass e ele deixa colocar o Create(nil) e o TObject não permite.
Esta é a chamada do formulário:
Olá, Judeir, tudo bem?
Peço desculpas pela demora.
Tente fazer um teste com esse código:
Abraço!
Feliz ano novo e parabéns pelos seus artigos de Delphi e explicação de como instar o Delphi 10.3.2 CE. Com esta explicação consegui instar o Delphi 10.3.3 CE. Vou testar André muito obrigado pelo retorno.
Disponha, Judeir!
Feliz ano novo pra você também.
Boa tarde André fiz o teste e acontece um erro dizendo que o objeto não existe. Coloquei o TfmCliente no TControllerFormsGeral(‘TfrmCliente’) no botão de chamada do cliente no formulário principal.
Estou usado o Delphi 7.
Vou entrar em contato, Judeir.
ok agradeço.