Реализация пользовательских шаблонов посетителей - PullRequest
0 голосов
/ 19 февраля 2019

Я пытаюсь реализовать своего рода шаблон посетителя.В большинстве примеров в Интернете показан класс посетителя с методом «посещения» и множественными перегрузками этого метода.В этом случае я назвал мой метод посещений CalculateFee (это семантическое дело) с его перегрузками.Пока все нормально, но теперь мне нужно снова реализовать посетителя, чтобы выполнить другой метод "CalculateExtraCharge", поэтому я добавил еще один метод с именем CalculateExtraCharge с его перегрузками.Но теперь у меня есть два вопроса

1) Это неправильная реализация шаблона?

2) Должен ли я всегда называть свой метод "визитом"?

Вот краткий обзор моего кода, я опустил некоторые его части, чтобы сосредоточиться только на том, что важно для моего вопроса.

    public class CreditCard : IPaymentMethod
    {
        public decimal Amount { get; set; }

        public decimal GetFee(IPaymentCalculationsVisitor visitor)
        {
            return visitor.CalculateFee(this);
        }

        public decimal GetExtraCharge(IPaymentCalculationsVisitor visitor)
        {
            return visitor.CalculateExtraCharge(this);
        }

    }

    public class Check : IPaymentMethod
    {
        public decimal Amount { get; set; }

        public decimal GetFee(IPaymentCalculationsVisitor visitor)
        {
            return visitor.CalculateFee(this);
        }

        public decimal GetExtraCharge(IPaymentCalculationsVisitor visitor)
        {
            return visitor.CalculateExtraCharge(this);
        }
    }

    public interface IPaymentCalculationsVisitor
    {
        decimal CalculateFee(CreditCard creditCard);
        decimal CalculateFee(Check check);

        decimal CalculateExtraCharge(CreditCard creditCard);
        decimal CalculateExtraCharge(Check check);
    }

    public class PaymentCalculationsVisitor: IPaymentCalculationsVisitor
    {

        public decimal CalculateFee(CreditCard creditCard)
        {
            return creditCard.Amount * 0.15m;

        }

        public decimal CalculateFee(Check check)
        {
            return check.Amount * 0.10m;
        }

        public decimal CalculateExtraCharge(CreditCard creditCard)
        {
            return 15;
        }

        public decimal CalculateExtraCharge(Check check)
        {
            return 10;
        }

    }

    public class PaymentProcessor
    {

        public void ProcessPayment()
        {
            var paymentMethods = new List<IPaymentMethod>()
            {
                new CreditCard(),
                new Check()
            };

            var calculationsVisitor = new PaymentCalculationsVisitor();

            foreach (var paymentMethod in paymentMethods)
            {

                //First i need to get the fee
                var fee = paymentMethod.GetFee(calculationsVisitor);

                //Then i do do some other stuff, validations, other calculations etc

                //Finally i get the extra charge
                var extraCharge = paymentMethod.GetExtraCharge(calculationsVisitor);
            }

        }

    }

Ответы [ 2 ]

0 голосов
/ 19 февраля 2019

2) Должен ли я всегда называть свой метод "визитом"?

Нет, метод имени более специфичен для конкретного домена.

1) Это неправильная реализация шаблона?

Глядя на вашу реализацию, я обнаружил, что она немного отличается.

public class CreditCard : IPaymentMethod
{
    public decimal Amount { get; set; }

    public decimal GetFee(IPaymentCalculationsVisitor visitor)
    {
        return visitor.CalculateFee(this);
    }

    public decimal GetExtraCharge(IPaymentCalculationsVisitor visitor)
    {
        return visitor.CalculateExtraCharge(this);
    }
}

Одним из объектно-ориентированного программирования является инкапсуляция, когда объект должен своим данным (не подвергается воздействию извне).world).
С помощью шаблона Visitor мы можем предоставить дополнительную функциональность объекту, не раскрывая его данные снаружи.

Поскольку внутренние данные не отображаются снаружи объекта, посетителю необходимо «посетить объект», когда объект сможет предоставить посетителю требуемые значения, не раскрывая эти значения снаружи (не создавая эти значения).public).

В случае вопроса мы можем перевести калькулятор (посетителя) в класс CreditCard, где калькулятор будет принимать только требуемые данные в качестве аргументов (обратите внимание только на требуемые значения - не на весь объект).

public class CreditCard : IPaymentMethod
{
    // Following OOP principles and keep data private
    private decimal _amount;

    public CreditCard(decimal amount) => _amount;

    public decimal GetFee(IPaymentCalculationsVisitor visitor)
    {
        return visitor.CalculateFee(_amount); // provide only required data
    }

    public decimal GetExtraCharge(IPaymentCalculationsVisitor visitor)
    {
        return visitor.CalculateExtraCharge(_amount); // provide only required data
    }
}

При таком подходе класс калькулятора (посетителя) не будет зависеть от классов, которые он может посещать.На самом деле он может посещать любой класс, который может предоставить необходимую информацию.

В вашем конкретном случае, когда CreditCard предоставляет данные (имеющие публичное свойство Amount) - вы можете удалить лишний шаг и передать объект кредитной карты прямо врасчеты

 public void ProcessPayment()
 {
     var paymentMethods = new List<IPaymentMethod>()
     {
            new CreditCard(),
            new Check()
     };

     var calculations = new PaymentCalculationsVisitor();

     foreach (var paymentMethod in paymentMethods)
     {
        //First i need to get the fee
        var fee = calculations.GetFee(paymentMethod);

        //Then i do do some other stuff, validations, other calculations etc

        //Finally i get the extra charge
        var extraCharge = calculations.GetExtraCharge(paymentMethod);
    }
}
0 голосов
/ 19 февраля 2019

1) Это неправильная реализация шаблона?

Нет, это все еще шаблон посетителя GoF.Возможность IPaymentCalculationsVisitor посещать два разных метода не меняет природу шаблона.Однако, поскольку он объединяет логику для двух разных посещений, вы можете подумать о принципах SOLID.

  • Являются ли оба метода частью Single Responsibility , т.е. будут ли они обаизменить по тем же причинам или один метод потенциально изменится независимо от другого?
  • Будут ли потенциальные клиенты всегда зависеть от обоих методов? Разделение интерфейса будет разъединять CalculateFee и CalculateExtraCharge, чтобы клиенты могли иметь одно без другого.

Обратите внимание, что шаблон посетителя может позволить добавить новое поведение в IPaymentMethod Иерархия типов без необходимости изменения дочерних классов, таких как CreditCard и Check.Разделив интерфейс посетителя на FeeVisitor и ExtraChargeVisitor, можно передать оба метода в один метод посещения.

2) Должен ли я всегда называть свой метод "визитом"?

Нет, в любом шаблоне проектирования вы можете назвать ваши методы как угодно.Очень важно, чтобы код имел значение для вас и для его домена.Ради общего словаря с другими разработчиками, вы можете рассмотреть возможность добавления документации, используя опубликованную терминологию шаблона, если вы думаете, что это прояснит цель кода.

...