My favorite design pattern?
Romain Berthon
#JobHacker
@RomainTrm
romainberthon.blog

Before we begin
public record SomeType(
int? Property1,
int Property2);
Context(s)
Billing
Bill generation
Payment method
string Print(...)
A poor design
public record PaymentMethod(
bool IsBankCheck,
string? BankCheckNumber,
bool IsCreditCard,
DateTime? ExpirationDate,
bool IsCash);
A poor design
public static string Print(PaymentMethod paymentMethod)
{
if (paymentMethod.IsBankCheck
&& string.IsNullOrEmpty(paymentMethod.BankCheckNumber))
return $"Bank check n°{paymentMethod.BankCheckNumber}";
if (paymentMethod.IsCreditCard
&& paymentMethod.ExpirationDate.HasValue)
return "Credit card (valid until " +
$"{paymentMethod.ExpirationDate.Value:MM/yy})";
if (paymentMethod.IsCash)
return "Cash";
throw new InvalidOperationException();
}
A better design
public interface IPaymentMethod { }
public record BankCheck(string BankCheckNumber)
: IPaymentMethod;
public record CreditCard(DateTime ExpirationDate)
: IPaymentMethod;
public record Cash : IPaymentMethod;
A better design
public static string Print(IPaymentMethod paymentMethod)
{
return paymentMethod switch
{
BankCheck bankCheck
=> $"Bank check n°{bankCheck.BankCheckNumber}",
CreditCard creditCard
=> "Credit card (valid until " +
$"{creditCard.ExpirationDate:MM/yy})",
Cash => "Cash",
_ => throw new InvalidOperationException()
};
}
A new payment method
public interface IPaymentMethod { }
public record BankCheck(string BankCheckNumber)
: IPaymentMethod;
public record CreditCard(DateTime ExpirationDate)
: IPaymentMethod;
public record Cash : IPaymentMethod;
public record PayPaulAccount(string email)
: IPaymentMethod;
A new payment method
public static string Print(IPaymentMethod paymentMethod)
{
return paymentMethod switch
{ BankCheck bankCheck
=> $"Bank check n°{bankCheck.BankCheckNumber}",
CreditCard creditCard
=> "Credit card (valid until " +
$"{creditCard.ExpirationDate:MM/yy})",
Cash => "Cash",
_ => throw new InvalidOperationException()
};
}
Visitor pattern
public interface IPaymentMethod
{
T Accept<T>(IPaymentMethodVisitor<T> visitor);
}
public interface IPaymentMethodVisitor<out T>
{
T Handle(BankCheck bankCheck);
T Handle(CreditCard creditCard);
T Handle(Cash _);
}
Visitor pattern
public record BankCheck(string BankCheckNumber)
: IPaymentMethod
{
public T Accept<T>(IPaymentMethodVisitor<T> visitor)
=> visitor.Handle(this);
}
public record CreditCard(DateTime ExpirationDate)
: IPaymentMethod
{
public T Accept<T>(IPaymentMethodVisitor<T> visitor)
=> visitor.Handle(this);
}
// ...
// You got it !
Visitor pattern
public static string Print(IPaymentMethod paymentMethod)
=> paymentMethod.Accept(new PrintPaymentMethod());
public class PrintPaymentMethod : IPaymentMethodVisitor<string>
{
public string Handle(BankCheck bankCheck)
=> $"Bank check n°{bankCheck.BankCheckNumber}";
public string Handle(CreditCard creditCard)
=> "Credit card (valid until " +
$"{creditCard.ExpirationDate:MM/yy})";
public string Handle(Cash _) => "Cash";
}
Visitor pattern: its true power
public record PayPaulAccount(string Email)
: IPaymentMethod
{
public T Accept<T>(IPaymentMethodVisitor<T> visitor)
=> visitor.Handle(this);
}
public interface IPaymentMethodVisitor<out T>
{ T Handle(BankCheck bankCheck);
T Handle(CreditCard creditCard);
T Handle(Cash _); }
public record PayPaulAccount(string Email)
: IPaymentMethod
{
public T Accept<T>(IPaymentMethodVisitor<T> visitor)
=> visitor.Handle(this);
}
public interface IPaymentMethodVisitor<out T>
{ T Handle(BankCheck bankCheck);
T Handle(CreditCard creditCard);
T Handle(Cash _); T Handle(PayPaulAccount payPaulAccount);
}
Visitor pattern: its true power
public class PrintPaymentMethod : IPaymentMethodVisitor<string>
{ public string Handle(BankCheck bankCheck)
=> $"Bank check n°{bankCheck.BankCheckNumber}";
public string Handle(CreditCard creditCard)
=> "Credit card (valid until " +
$"{creditCard.ExpirationDate:MM/yy})";
public string Handle(Cash _) => "Cash"; }
Visitor pattern: its true power
public class PrintPaymentMethod : IPaymentMethodVisitor<string>
{ public string Handle(BankCheck bankCheck)
=> $"Bank check n°{bankCheck.BankCheckNumber}";
public string Handle(CreditCard creditCard)
=> "Credit card (valid until " +
$"{creditCard.ExpirationDate:MM/yy})";
public string Handle(Cash _) => "Cash";
public string Handle(PayPaulAccount payPaulAccount)
=> $"PayPaul ({payPaulAccount.Email})";
}
Visitor pattern: its true power
Thank you!
Romain Berthon
#JobHacker
@RomainTrm
romainberthon.blog

My favorite design pattern?
By Romain Berthon
My favorite design pattern?
- 144