Предположим, у меня есть класс Customer
. Клиент может иметь несколько видов баллов лояльности. За одну акцию клиент может собирать Rewards
. В другом случае покупатель может собирать Miles
. И нет фиксированного количества видов очков лояльности, для которых предназначен Customer
. Я уверен, что это распространенный случай использования. Подходит ли шаблон декоратора, приведенный ниже примера кода?
public interface ICustomer
{
void Display();
}
public class SimpleCustomer : ICustomer
{
public void Display()
{
Console.WriteLine("I am simple customer");
}
}
public abstract class CustomerDecorator : ICustomer
{
protected ICustomer customer;
public CustomerDecorator(ICustomer customer)
{
this.customer = customer ?? throw new ArgumentNullException("customer");
}
public abstract void Display();
}
public class RewardsDecorator : CustomerDecorator
{
private int rewards;
public RewardsDecorator(ICustomer customer, int rewards) : base(customer)
{
this.rewards = rewards;
}
public override void Display()
{
Console.WriteLine("Now I have " + rewards.ToString() + " rewards");
}
}
public class MilesDecorator : CustomerDecorator
{
private int miles;
public MilesDecorator(ICustomer customer, int miles) : base(customer)
{
this.miles = miles;
}
public override void Display()
{
Console.WriteLine("Now I have " + miles.ToString() + " miles");
}
}
Я не думаю, что Decorator — это шаблон, который вы ищете.
Кроме того, ваш код не выглядит реализацией шаблона Decorator. Вы не добавляете никакой функциональности к единственной функции. Вы просто переопределяете это. Но добавление к существующей функции — вот что такое шаблон Decorator.
Мой подход будет шаблоном состояния/стратегии. Есть разные виды наград. И у клиента есть один или несколько из них. Эти вознаграждения могут иметь общий интерфейс и предоставлять различные реализации. Клиент (или подкласс или составной объект RewardedCustomer
) должен иметь список или карту этих вознаграждений.
Не думайте, что шаблон Decorator делает то, что вы хотите. Decorator добавляет новые функции поверх исходного класса. В типичном вики-примере мы можем добавить полосу прокрутки, строку меню, наложения и другие компоненты пользовательского интерфейса поверх холста. Итак, чтобы сделать правильное окно браузера, у вас будет:
public class Canvas
public class ScrollableCanvas
public class OverlayedCanvas
etc.
Так что мы добавляем больше функциональности к оригинальному Canvas.
Чтобы решить вашу проблему, у вас должно быть что-то вроде:
public abstract class LoyaltyProgramAccount {...}
public class RewardAccount extends LoyaltyProgramAccount {...}
public class MilesAccount extends LoyaltyProgramAccount {...}
А затем добавьте регистровое перечисление:
public enum LoyaltyProgramTypes {
miles,
reward,
}
а затем пусть пользователь будет:
public class Customer {
private List<LoyaltyProgramTypes, LoyaltyProgramAccount> accounts;
public void openAccount(LoyaltyProgramTypes type, LoyaltyProgramAccount account) {
accounts.put(type, account);
}
...
}
Я бы использовал шаблон посетителя, это идеально подходит для вашей ситуации. Это позволит вам четко разделить расчеты вознаграждений для разных типов клиентов и выполнять операции со всеми поддерживаемыми типами вознаграждений.
class Program
{
static void Main(string[] args)
{
MilesCustomer customer = new MilesCustomer();
ICustomerVisitor<int> visitor = new MilesCalculation(10);
var miles = customer.Visit(visitor);
visitor = new RewardsCalucation(100);
var rewards = customer.Visit(visitor);
}
}
public interface ICustomerVisitor<T>
{
T Visit(SimpleCustomer cusomter);
T Visit(RewardsCustomer cusomter);
T Visit(MilesCustomer cusomter);
}
public abstract class Customer
{
public Customer()
{
TotalMoneySpent = 10;
}
public int TotalMoneySpent { get; private set; }
public abstract T Visit<T>(ICustomerVisitor<T> visitor);
public virtual void Display()
{
Console.WriteLine("I am simple customer");
}
}
public class RewardsCalucation : ICustomerVisitor<int>
{
private int _rewardsPerDollar;
public RewardsCalucation(int rewardsPerDollar) => _rewardsPerDollar = rewardsPerDollar;
public int Visit(SimpleCustomer cusomter)
{
return 0;
}
public int Visit(RewardsCustomer cusomter)
{
return cusomter.TotalMoneySpent * _rewardsPerDollar;
}
public int Visit(MilesCustomer cusomter)
{
return 0;
}
}
public class MilesCalculation : ICustomerVisitor<int>
{
private int _milesPerDollar;
public MilesCalculation(int milesPerDollar) => _milesPerDollar = milesPerDollar;
public int Visit(SimpleCustomer cusomter)
{
return 0;
}
public int Visit(RewardsCustomer cusomter)
{
return 0;
}
public int Visit(MilesCustomer cusomter)
{
return cusomter.TotalMoneySpent * _milesPerDollar;
}
}
public class SimpleCustomer : Customer
{
public override T Visit<T>(ICustomerVisitor<T> visitor)
{
return visitor.Visit(this);
}
}
public class RewardsCustomer : Customer
{
public override T Visit<T>(ICustomerVisitor<T> visitor)
{
return visitor.Visit(this);
}
}
public class MilesCustomer : Customer
{
public override T Visit<T>(ICustomerVisitor<T> visitor)
{
return visitor.Visit(this);
}
}