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!

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.
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.
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;
}Public Function IsLate(expectedDelivery As DateTime) As Boolean
Return _systemClock.Now() > expectedDelivery
End FunctionO 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.
Derek apresenta a interface IDeliveryTimingPolicy:
public interface IDeliveryTimingPolicy
{
bool IsLate(Shipment shipment);
}Public Interface IDeliveryTimingPolicy
Function IsLate(shipment As Shipment) As Boolean
End InterfaceEle então cria duas implementações:
Política de tempo de entrega padrão
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);
}Public Function IsLate(shipment As Shipment) As Boolean
Return _clock.Now() > shipment.DeliveryDate.AddMinutes(30)
End FunctionAgora, na classe Shipment, usamos despacho duplo :
public bool IsLate(IDeliveryTimingPolicy policy)
{
return policy.IsLate(this);
}Public Function IsLate(ByVal policy As IDeliveryTimingPolicy) As Boolean
Return policy.IsLate(Me)
End FunctionEste é 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.
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 bufferDim policy = New BufferedDeliveryTimingPolicy(TimeSpan.FromMinutes(30))
Dim shipment = New Shipment With {.DeliveryDate = DateTime.UtcNow.AddMinutes(-15)}
Assert.False(shipment.IsLate(policy)) ' Because of 30-minute bufferIsso 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.
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);
}
}Public MustInherit Class Shape
Public MustOverride Sub Accept(visitor As IVisitor)
End Class
Public Class Circle
Inherits Shape
Public Overrides Sub Accept(visitor As IVisitor)
visitor.Visit(Me)
End Sub
End ClassAqui, 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#.
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)Public Function IsSatisfiedBy(shipment As Shipment) As BooleanEm seguida, a classe Shipment os avalia da seguinte forma:
public bool CanShip(IEnumerable<IShipmentReadinessRule> rules)
{
return rules.All(rule => rule.IsSatisfiedBy(this));
}Public Function CanShip(rules As IEnumerable(Of IShipmentReadinessRule)) As Boolean
Return rules.All(Function(rule) rule.IsSatisfiedBy(Me))
End FunctionEste 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.
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);Dim rules = LoadRulesForCustomer(customerId)
Dim 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.
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.
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.
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.
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!
Join our newsletter, you’ll get exclusive access on article updates. We value your privacy