IRONSOFTWAREHOME

Despacho Duplo em C#: Quando a Injeção de Dependências Faz Sentido – Explicado através do Exemplo de Domain-Driven Design de Derek Comartin

Double Dispatch in DDD: When Injecting Dependencies Makes Sense

Derek Comartin

8m 44s

No campo da linguagem de programação C#, o despacho duplo é frequentemente mal compreendido ou subutilizado. É uma técnica poderosa que permite o comportamento polimórfico entre dois objetos envolvidos, especialmente ao lidar com o comportamento entre classes derivadas.

Em seu vídeo " Double Dispatch in DDD: When Injecting Dependencies Makes Sense ", Derek Comartin explica como o despacho duplo se encaixa perfeitamente no Domain-Driven Design (DDD). Analisaremos seus exemplos em detalhes, mostrando como eles podem reduzir a carga de manutenção, simplificar o código existente e até mesmo imitar padrões como o padrão Visitor, comumente visto em outras linguagens.

Se você já lidou com as limitações do despacho simples em C#, ou tentou determinar o comportamento com base tanto na instância de um objeto quanto em uma estratégia ou regra injetada, este artigo ajudará a esclarecer como o despacho duplo em C# pode ser usado de forma eficaz.

Por que injetar comportamento faz sentido em modelos de domínio

Derek começa por destacar um dogma comum no DDD: o de que seu modelo de domínio não deve ter nenhuma dependência. Mas isso nem sempre é prático ou útil. Ao modelar o comportamento, pode ser necessário inserir lógica, como regras, políticas ou estratégias. É aqui que o despacho duplo ajuda: você pode passar um conceito de domínio, como uma política, para o objeto de domínio e deixar que a avaliação baseada em método seja tratada externamente - mantendo, ainda assim, o controle final sob o objeto de domínio.

Esse padrão faz sentido quando você considera a que está se acoplando: ao comportamento do domínio, e não à infraestrutura.

O mau exemplo: lógica codificada diretamente no domínio.

Para demonstrar o problema, Derek começa com uma classe Shipment que contém lógica codificada para determinar o atraso:

public bool IsLate(DateTime expectedDelivery)
{
    return _systemClock.Now() > expectedDelivery;
}

O código existente é simples, mas inflexível. Depende de decisões tomadas em tempo de compilação e acopla fortemente o comportamento à classe. Alterar a regra exigiria alterar o modelo de domínio, e os testes são mais difíceis, pois dependem do tempo.

A Refatoração: Utilizando Políticas e Despacho Duplo

Derek apresenta a interface IDeliveryTimingPolicy:

public interface IDeliveryTimingPolicy
{
    bool IsLate(Shipment shipment);
}

Ele então cria duas implementações:

  1. Política de tempo de entrega padrão

  2. Política de tempo de entrega em buffer

Essas classes recebem o Shipment e retornam um valor booleano com base em sua regra. Segue abaixo um trecho de código da BufferedDeliveryTimingPolicy:

public bool IsLate(Shipment shipment)
{
    return _clock.Now() > shipment.DeliveryDate.AddMinutes(30);
}

Agora, na classe Shipment, usamos despacho duplo :

public bool IsLate(IDeliveryTimingPolicy policy)
{
    return policy.IsLate(this);
}

Este é um exemplo clássico de despacho duplo: o primeiro despacho chama IsLate() na remessa e passa a política; O segundo despacho está chamando IsLate() na política com a remessa como parâmetro. Dois objetos estão envolvidos, cada um participando na determinação do comportamento - algo que o despacho único não consegue alcançar.

Benefícios em Testes e Flexibilidade

Essa abordagem resulta em código altamente testável e determinístico. Derek mostra exemplos usando políticas padrão e em buffer, onde os dados de teste são controlados e o tipo de tempo de execução de cada objeto determina o comportamento.

var policy = new BufferedDeliveryTimingPolicy(TimeSpan.FromMinutes(30));
var shipment = new Shipment { DeliveryDate = DateTime.UtcNow.AddMinutes(-15) };
Assert.False(shipment.IsLate(policy)); // Because of 30-minute buffer

Isso demonstra como o comportamento em tempo de execução pode ser alterado sem modificar o modelo de domínio. Você obtém um comportamento polimórfico baseado na natureza dinâmica da política, mantendo a integridade do domínio.

Despacho Duplo vs Padrão Visitor em C#

O exemplo de Derek se assemelha ao padrão Visitor, onde você define um conjunto de operações a serem executadas em objetos sem alterar sua estrutura. Normalmente, no padrão de visitante, você veria algo como:

public abstract class Shape
{
    public abstract void Accept(IVisitor visitor);
}

public class Circle : Shape
{
    public override void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }
}

Aqui, os métodos Accept() e Visit() são coordenados entre dois tipos - o objeto e o visitante - assim como Derek faz com Shipment e IDeliveryTimingPolicy. Esse padrão ajuda a implementar o comportamento de despacho múltiplo mesmo em uma linguagem de despacho único como C#.

Compondo regras com conjuntos de políticas

Em outro exemplo a seguir, Derek mostra como várias regras podem ser avaliadas por meio de uma coleção. Ele introduz regras como:

  • RegraHasValidDestination

  • Regra de Pacotes Empacotados

  • Regra de não enviado

Cada um implementa IShipmentReadinessRule com:

public bool IsSatisfiedBy(Shipment shipment)

Em seguida, a classe Shipment os avalia da seguinte forma:

public bool CanShip(IEnumerable<IShipmentReadinessRule> rules)
{
    return rules.All(rule => rule.IsSatisfiedBy(this));
}

Este padrão de aceitação vazio permite avaliar várias regras de domínio dinamicamente. Se você armazenar regras na configuração (especialmente em um aplicativo multi-tenant), poderá criar uma nova lista em tempo de execução e passá-la para o envio.

Regras dinâmicas em aplicações multi-inquilino

Derek destaca que, em aplicações multi-inquilino, o conjunto de regras pode variar de cliente para cliente. Você pode buscar uma lista de políticas no armazenamento e injetá-las dinamicamente:

var rules = LoadRulesForCustomer(customerId);
var canShip = shipment.CanShip(rules);

Isso demonstra como o despacho dinâmico e a tomada de decisões em tempo de execução podem ser integrados à sua aplicação. O comportamento desejado é alcançado alterando a configuração, não o modelo.

Esclarecendo equívocos: nem todas as dependências são ruins.

Na parte final, Derek aborda a ideia errada de que injetar qualquer coisa em um domínio é ruim. Ele enfatiza que o que importa é o que você está injetando. Injetar comportamento de domínio, como especificações ou políticas, não é o mesmo que injetar infraestrutura.

O modelo de domínio continua sendo o ponto de partida. A decisão é dela, mas pode ser delegada a outros objetos, desde que estejam dentro do mesmo contexto de domínio.

Conclusão: Por que Despacho Duplo C# é Poderoso em DDD

Derek conclui nos incentivando a pensar criticamente: não tenhamos medo de visitas ao vazio, aceitação pública do vazio virtual ou padrões semelhantes quando eles trazem clareza e facilidade de manutenção. Ao injetar lógica de negócios de forma controlada, você ganha flexibilidade e precisão.

Portanto, seja você trabalhando em uma nova classe ou refatorando uma base de código existente, o despacho duplo em C# oferece uma maneira clara de separar responsabilidades, mantendo o foco no domínio.

Se você estiver usando políticas, especificações ou até mesmo construindo um padrão de visitante sem perceber, já está perto de implementar o despacho duplo. Compreender isso lhe dá mais controle sobre como o código se comporta em tempo de execução, melhorando a testabilidade e a adaptabilidade.

Conclusão

Resumindo: o despacho duplo em C# pode ser uma solução elegante e prática para injetar lógica de domínio, preservar o encapsulamento e suportar comportamento flexível. Quando usado com padrões como visitante, despacho múltiplo e uso dinâmico de palavras-chave (com cuidado), permite escrever modelos de domínio expressivos e robustos.

Então, da próxima vez que você chamar shipping.IsLate(policy) - saiba que você está aproveitando um padrão fundamental que aproxima o C# de um design verdadeiramente polimórfico.

Exemplo de dica: Se você estiver criando uma classe PurchaseOrder e quiser determinar se um Item pode ser adicionado com base em uma política, tente passar a política para o PurchaseOrder e deixe a política visitar o pedido. Isso é despacho duplo em ação.

Assista ao vídeo completo no canal dele no YouTube e obtenha mais informações sobre o assunto.

Earn More by Sharing What You Love

Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Let's Stay in Touch!

Join our newsletter, you’ll get exclusive access on article updates. We value your privacy

Key in blue circle

Obtenha sua chave de avaliação gratuita de 30 dias instantaneamente.

Your trial license will be sent to your email address

Sem limitações. 100% desbloqueado. Sem cartão de crédito.

OR
bullet_checkedNão é necessário cartão de crédito nem criação de conta.Sem limitações. 100% desbloqueado. Sem cartão de crédito.
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried Iron Suite
Agende sua consulta sem compromisso.
Preencha o formulário abaixo ou envie um e-mail para sales@ironsoftware.com
Os seus dados serão sempre mantidos em sigilo.
Aprovado por milhões de engenheiros em todo o mundo.
Logotipos dos clientes da Iron Software
Obtenha sua chave de avaliação gratuita de 30 dias instantaneamente.
Não é necessário cartão de crédito nem criação de conta.