簡體   English   中英

自定義訪問者模式實現

[英]Custom visitor pattern implementation

我正在嘗試實現一種訪客模式。 Web上的大多數示例都顯示了具有“訪問”方法和該方法的多個重載的訪問者類。 在這種情況下,我將其重載稱為“訪問”方法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)我應該始終將我的方法稱為“訪問”嗎?

否,以更特定於域的方式命名方法。

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);
    }
}

面向對象編程之一是封裝,其中對象欠其數據(不暴露給外界)。
使用Visitor模式,我們可以為對象提供額外的功能,而無需將其數據暴露在外部。

由於內部數據沒有暴露給對象的外部,因此訪問者需要“訪問對象內部”,其中對象將能夠向訪問者提供所需的值,而不會在外部暴露這些值(而不會公開這些值)。

對於問題的情況,我們可以將CreditCard (visitor)傳遞到CreditCard類中,在該類中, 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);
    }
}

1)這是該模式的錯誤實現嗎?

不,這仍然是GoF訪客模式。 IPaymentCalculationsVisitor訪問兩種不同方法的能力不會改變模式的性質。 因為它結合了兩次不同訪問的邏輯,所以您可能要考慮SOLID原則。

  • 兩種方法是否都屬於“ 單一職責” ,即它們是否會因為相同的原因而改變,或者一種方法可能獨立於另一種方法而改變?
  • 潛在客戶會始終依賴這兩種方法嗎? 接口隔離將使CalculateFeeCalculateExtraCharge解耦,因此客戶端可以擁有一個而沒有另一個。

請注意,訪問者模式可以允許將新行為添加到IPaymentMethod類型層次結構中,而無需修改子類(例如CreditCardCheck 通過將訪問者界面分為FeeVisitorExtraChargeVisitor ,可以將兩者都傳遞到單個訪問方法中。

2)我應該始終將我的方法稱為“訪問”嗎?

不,在任何設計模式下,您都可以根據自己的喜好命名方法。 最重要的是,代碼對您及其領域具有意義。 為了與其他開發人員共同使用詞匯,如果您認為可以澄清代碼的意圖,則可以考慮使用該模式的已發布術語添加文檔。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM