Tecnologia além do código.
Régys Borges da Silveira
← Voltar para explorar os artigos

Design Patterns em Delphi 13: Strategy e Observer – Flexibilidade e Reatividade

Neste artigo 14 tópicos

Um dos maiores desafios em sistemas de gestão (ERPs) é lidar com regras de negócio que mudam conforme o contexto — como o cálculo de um imposto que varia por estado ou o envio de notificações que devem ocorrer sempre que um estoque atinge o nível crítico.

Neste artigo, veremos como o Strategy nos permite trocar algoritmos em tempo de execução e como o Observer estabelece uma comunicação desacoplada entre objetos no Delphi 13.

1. Strategy: Algoritmos Intercambiáveis

No desenvolvimento de software corporativo, é comum encontrarmos métodos que tentam prever todas as variações de uma regra de negócio. O padrão Strategy ataca diretamente o acoplamento causado por essas decisões centralizadas, encapsulando cada variação em uma classe separada e tornando-as intercambiáveis.

O Problema: O “Método Deus” e a Rigidez Algorítmica

Imagine um sistema de faturamento que precisa calcular o imposto de acordo com o estado ou o tipo de produto.

  • Modo Antigo: Um método CalcularImposto contendo um case ou uma sequência de if com centenas de linhas.
  • Impacto no Delphi 13: Cada vez que uma alíquota muda ou um novo estado é adicionado, você precisa alterar o código core do sistema, recompilar e realizar testes de regressão em todo o módulo. Isso viola o Princípio Aberto/Fechado (OCP), que dita que uma classe deve estar aberta para extensão, mas fechada para modificação.

A Solução: Injeção de Algoritmos

Com o Strategy, definimos uma interface comum (ex: IImpostoStrategy) e implementamos cada regra em sua própria unidade (ex: TImpostoICMS, TImpostoISS).

Por que isso transforma seu código?

  1. Isolamento de Responsabilidade: O objeto de negócio (Contexto) não sabe como o cálculo é feito, apenas o que deve ser feito.
  2. Extensibilidade sem Risco: Para adicionar uma nova regra, basta criar uma nova classe que implemente a interface. O código existente permanece intocado e seguro.
  3. Facilidade de Testes: Você pode testar cada estratégia de forma isolada através de testes unitários, garantindo que o cálculo de “ICMS” esteja correto sem precisar processar uma venda completa.

No Delphi 13, a combinação de Strategy com Dependency Injection (DI) permite que o sistema decida, em tempo de execução (via configuração ou metadados), qual algoritmo deve ser injetado no motor de regras, conferindo uma flexibilidade sem precedentes à aplicação.

2. Observer: Comunicação Orientada a Eventos

O padrão Observer define uma dependência um-para-muitos entre objetos, de modo que quando um objeto muda de estado, todos os seus dependentes são notificados e atualizados automaticamente.

Implementação Técnica: O “Publisher-Subscriber” no Delphi 13

No Delphi, o Observer é a alternativa profissional aos eventos (TNotifyEvent) quando você precisa que múltiplos interessados reajam a uma única mudança.

unit Regys.Patterns.Observer;

interface

uses System.Generics.Collections, System.SysUtils;

type
  // 1. O Contrato do Observador
  IObserver = interface
    ['{D1E2F3A4-B5C6-4789-A0B1-C2D3E4F5A6B7}']
    procedure Update(const AMsg: string);
  end;

  // 2. O Sujeito (O objeto que será observado)
  TEstoque = class
  private
    FObservadores: TList<IObserver>;
    FQuantidade: Integer;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Adicionar(AObserver: IObserver);
    procedure MudarQuantidade(const ANovaQtd: Integer);
  end;

implementation

{ TEstoque }
constructor TEstoque.Create;
begin
  FObservadores := TList<IObserver>.Create;
end;

procedure TEstoque.Adicionar(AObserver: IObserver);
begin
  FObservadores.Add(AObserver);
end;

procedure TEstoque.MudarQuantidade(const ANovaQtd: Integer);
begin
  FQuantidade := ANovaQtd;
  // Notifica todos os interessados
  for var LObserver in FObservadores do
    LObserver.Update('Estoque alterado para: ' + IntToStr(FQuantidade));
end;

destructor TEstoque.Destroy;
begin
  FObservadores.Free;
  inherited;
end;

end.

3. Aplicação no Dia a Dia: Regras de Negócio e Interfaces Reativas

No quotidiano de um programador Delphi 13, o Strategy e o Observer são os alicerces para evitar que o código se torne um “legado difícil” em poucos meses. Eles permitem que o software cresça organicamente, respondendo a mudanças legislativas e de interface com o mínimo de impacto.

A. Strategy: Motores de Impostos e Promoções Dinâmicas

Um dos cenários mais críticos em ERPs é a volatilidade das regras fiscais e comerciais.

  • Cenário de Uso: Imagine um motor de descontos onde, hoje, o desconto é fixo, mas amanhã pode ser progressivo ou baseado na fidelidade do cliente.
  • A Transição Clean: Em vez de poluir a classe TVenda com lógica de descontos, você injeta um IDescontoStrategy.
  • Vantagem: O sistema pode escolher a estratégia em tempo de execução com base no perfil do cliente ou numa configuração do banco de dados, sem que a classe de Venda precise de um único if adicional.

B. Observer: Sincronização de UI e Micro-Serviços Internos

O Observer é a solução profissional para quando uma ação num ponto do sistema precisa de gerar uma “onda” de reações em outros módulos.

  • Exemplo Real: Num sistema de vendas, quando um StatusPedido muda para faturado:
    1. A UI precisa de atualizar a cor na grelha.
    2. O módulo de Logística precisa de gerar a guia de remessa.
    3. O módulo de CRM precisa de enviar um WhatsApp ao cliente.
  • Implementação no Delphi 13: O objeto TPedido não conhece a Logística nem o CRM. Ele simplesmente notifica os seus observadores. Cada módulo “inscreve-se” para observar o pedido, garantindo um desacoplamento total.

C. Strategy para Exportação e Integração (Gateways)

Se o seu sistema precisa de enviar dados para diferentes destinos (Arquivo TXT, API REST, JSON), utilize o Strategy. Defina a interface IExportStrategy. O seu código de exportação foca em quais dados enviar, enquanto as estratégias concretas focam no formato e no protocolo, facilitando a adição de novos destinos no futuro.

D. Observer para Auditoria e Logs (LGPD)

Com a necessidade de conformidade com a LGPD, o Observer pode ser usado para monitorizar alterações em campos sensíveis. Um TAuditoriaObserver pode observar as suas classes de domínio e, sempre que um método de alteração de dados for chamado, ele regista quem, quando e o que foi alterado, de forma totalmente transparente para a lógica de negócio principal.

4. Nuances Técnicas: Performance e Gerenciamento de Memória no Delphi 13

Implementar os padrões Strategy e Observer no Delphi 13 exige mais do que apenas criar classes; exige maestria no ciclo de vida dos objetos para evitar os temidos vazamentos de memória e garantir que a reatividade não comprometa a estabilidade da aplicação.

A. O Ciclo de Vida do Observer e o “Assinante Fantasma”

O maior risco do padrão Observer no Delphi é o vazamento de memória ou violação de acesso (Access Violation). Se um observador (Interface) for destruído mas o sujeito ainda mantiver uma referência para ele em sua lista interna, o sistema tentará notificar um objeto que não existe mais.

  • Solução Moderna: No Delphi 13, utilize TList<IInterface> para armazenar os observadores. A contagem de referência (ARC) garante que o objeto não será destruído enquanto estiver na lista.
  • Prática Sênior: Mesmo com interfaces, é recomendável implementar um método Remover(AObserver) e garantir que o observador se “desinscreva” no seu destructor ou no evento OnClose de um formulário, mantendo o controle total sobre o fluxo.

B. Performance e Polimorfismo no Strategy

O Strategy baseia-se em chamadas de métodos virtuais através de interfaces. No Delphi 13, isso introduz uma pequena indireção na tabela de métodos virtuais (VMT).

  • Impacto Real: Para 99% das regras de negócio (cálculos fiscais, validações, etc.), esse custo é medido em nanosegundos e é irrelevante.
  • Otimização: Em loops de altíssima performance (ex: processamento de milhões de registros em memória), prefira injetar a estratégia antes do início do loop, evitando que o código de decisão (qual estratégia usar) seja executado repetidamente dentro da iteração.

C. Observer e a Reatividade Multithread

Em aplicações modernas que utilizam TTask ou TThread, o sujeito pode notificar alterações a partir de uma thread secundária.

  • O Perigo: Se um observador for um componente de UI (um TLabel ou TGrid), tentar atualizá-lo diretamente de uma thread secundária causará instabilidade ou travamento.
  • A Abordagem Delphi 13: Utilize TThread.Queue ou TThread.Synchronize dentro do método Update do observador para garantir que a atualização da interface ocorra sempre na Main Thread, mantendo a aplicação fluida e segura.

D. Estratégias Estáticas vs. Dinâmicas

No Delphi 13, você pode otimizar o Strategy usando Anonymous Methods para estratégias muito simples, evitando a criação de uma unidade inteira para uma lógica de poucas linhas. Contudo, para regras complexas, a abordagem baseada em classes continua sendo a melhor escolha para manter a testabilidade e o desacoplamento.

E. Problemas Comuns

  • Memory Leaks no Observer: Este é o maior risco. Se um Observador (Interface) for destruído mas o Sujeito ainda o tiver na lista, você terá problemas. No Delphi 13, o uso de TList<IInterface> resolve isso, pois a referência cai para zero e o objeto é limpo. Contudo, é boa prática o observador se “desinscrever” no seu Destroy.
  • Inlining no Strategy: Como o Strategy faz chamadas polimórficas (virtuais), o compilador não consegue fazer o inline do código. O impacto é mínimo, mas em loops de milhões de iterações (processamento de imagem, por exemplo), isso deve ser considerado.
  • Observer Thread-Safe: Se o Sujeito for alterado em uma thread secundária, lembre-se de que os Observadores de UI devem usar TThread.Synchronize ou TThread.Queue ao processar a atualização.

Referências

FLICK, Stefan. Design Patterns with Delphi: Build enterprise-grade applications with modern Delphi and the right design patterns. 1. ed. Birmingham: Packt Publishing, 2024.

GAMMA, Erich; HELM, Richard; JOHNSON, Ralph; VLISSIDES, John. Padrões de Projeto: Soluções reutilizáveis de software orientado a objetos. 1. ed. Porto Alegre: Bookman, 2000.

NOGUEIRA, Rodrigo. Delphi e Clean Architecture: Princípios e práticas para software escalável. 2. ed. São Paulo: Editora Engenharia de Software, 2025.

TEIXEIRA, Marcello. Delphi High Performance: Build fast Delphi applications using concurrency, parallel programming and memory management. 1. ed. Birmingham: Packt Publishing, 2018.

Compartilhar por e-mail

0 comentário(s)

Dê-nos sua opinião, seu comentário ajuda o site a crescer e melhorar a qualidade dos artigos.

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.

Um próximo passo para cada interesse.

Continue estudando com os livros, acompanhe as videoaulas ou consulte as soluções para o seu projeto.

Descubra mais sobre Régys Borges da Silveira

Assine agora mesmo para continuar lendo e ter acesso ao arquivo completo.

Continuar lendo