[Delphi] Design Patterns GRASP – Creator

Olá, pessoal, como vão?
Sabemos que, em uma arquitetura orientada a objetos, a criação (ou instanciação) de objetos é uma das atividades mais comuns, além de ser bastante frequente. Porém, embora seja tão trivial, muitas vezes criamos estes objetos em classes erradas e não sabemos!
O propósito do Design Pattern Creator é nos ajudar a identificar as classes devidamente responsáveis pela criação de cada objeto. Acompanhe!

Introdução

Não há segredo em criar objetos. Basta declarar, instanciar e usá-lo, não é? Oras, pensando assim, por que existiria um padrão de projeto dedicado exclusivamente à este conceito?

Para responder à essa pergunta, considere o pequeno código a seguir:

Não há nada incorreto no código acima. No entanto, o meu objetivo é destacar uma linha do código que provavelmente passou despercebida: a seção uses.

Para que você possa criar um objeto de um determinado tipo, é necessário incluir a referência nessa seção, ou seja, a unit na qual a classe está declarada. A simples inclusão de uma referência pode parecer “inofensiva”, mas, caso essa referência seja utilizada em abundância ou em diferentes módulos (pacotes, projetos, bibliotecas, etc.), a arquitetura pode ser prejudicada em função do alto acoplamento.

Há três motivos que atestam essa afirmação:

  • Quando uma unit que é utilizada em vários locais do projeto sofre uma alteração, ocorre um efeito conhecido como Dependência Magnética (que já comentei no artigo sobre DRY). Em suma, esse efeito consiste no impacto que será causado em todas as classes que utilizam essa unit e que, por consequência, terão que ser recompiladas;
  • Quando se tem uma única unit que é referenciada em diversos pontos do código, provavelmente há um erro de abstração;
  • A inclusão demasiada de units na seção uses afeta a performance da IDE, já que produz problemas de referência circular (já tive experiência com isso!).

Creator

Para evitar todos estes problemas, portanto, podemos empregar as diretrizes recomendadas pelo Design Pattern Creator para identificar os responsáveis ideais pela criação de objetos. Desse modo, o acoplamento da arquitetura é reduzido e haverá menos instanciações arbitrárias, além de uma arquitetura mais previsível. A intenção é deixar claro que a criação de objetos não deve ocorrer em qualquer classe. Deve existir um motivo.

Observe que, diferente dos outros padrões de projeto, o Creator traz orientações, e não uma solução de arquitetura. Para o Creator, uma classe X só pode criar objetos da classe Y caso um (ou mais) dos requisitos abaixo seja atendido.

1) Classe X compõe ou agrega instâncias da classe Y

Por exemplo, uma classe de pedidos (X) que agrega instâncias dos itens do pedido (Y):

2) Classe X registra instâncias da classe Y

“Registrar”, neste contexto, se refere à persistência da instância (em um banco de dados, por exemplo), ou equivale simplesmente a “manter” uma instância em memória. No exemplo abaixo, usando o mesmo cenário do requisito anterior, a classe de pedidos (X) registra (mantém) uma instância da classe de clientes (Y).

Dessa forma, é possível obter o endereço de entrega do cliente com essa instrução:

Observe que também poderíamos obter o endereço de entrega como LPedido.Cliente.GetEnderecoEntrega, mas, para isso, teríamos que expor uma propriedade para acessar o objeto FCliente, violando o encapsulamento.

3) Classe X é “íntima” das instâncias da classe Y

Em outras palavras, a classe X deve ser “próxima” das instâncias da classe Y, ou seja, fazer parte do mesmo contexto. Na minha opinião, este é o requisito mais relevante, mas, ao mesmo tempo, é também o mais negligenciado.

No exemplo a seguir, a classe de transferência de arquivos (X) cria instâncias da classe TIdFTP (Y), já que estão intimamente vinculadas na perspectiva de contexto.

4) Classe X possui dados de inicialização da instância da classe Y

Se a classe X possui a maior parte ou todos os dados necessários para instanciar um objeto da classe Y, então a responsabilidade está correta. No código abaixo, a classe de contas a receber (X) preenche os atributos da classe de correção monetária (Y) pelo construtor antes de executar o método de cálculo:

Outra forma de preencher os atributos é através de propriedades:

That’s It!

Leitores, caso nenhum dos requisitos acima seja atendido, recomendo a revisão da arquitetura. Existe uma grande possibilidade de uma classe estar criando objetos indevidamente. Neste caso, novas classes intermediárias devem ser criadas; algumas responsabilidades devem ser extraídas para classes específicas; ou a criação de objetos deve ser movida para métodos compartilhados.

Uma pequena exceção destes requisitos é o Design Pattern Factory, do GoF, uma vez que a sua responsabilidade é justamente instanciar objetos.

Na verdade, eu gostaria de detalhar um pouco mais sobre cada um destes requisitos, mas o artigo ficaria muito extenso.

Espero que tenham gostado, pessoal.
Até breve!


 

André Celestino