Шаблон декоратора для класса Customer

Предположим, у меня есть класс 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");
        }
    }
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
359
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я не думаю, что 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);
    }
}

Другие вопросы по теме