[Delphi] Design Patterns GRASP – Controller

[Delphi] Design Patterns GRASP - Controller

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á?

 

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 me lembra algo…
Sim, 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:

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:

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:

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.

 

E quando o controle de um componente visual depende de uma regra de negócio?
Use o Controller também! Veja um exemplo abaixo:

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:

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!


 

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *