我正在尝试实现一种访客模式。 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);
}
}
}
答案 0 :(得分:3)
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模式,我们可以为对象提供额外的功能,而无需将其数据暴露在外部。
由于内部数据没有暴露给对象的外部,因此访问者需要“访问对象内部”,其中对象将能够向访问者提供所需的值,而不会在外部暴露这些值(而不会公开这些值)。
对于问题的情况,我们可以将Calculator(visitor)传递到CreditCard
类中,其中Calculator仅将必需的数据作为参数(仅注意必需的值-而非整个对象)。
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
}
}
使用这种方法,Calculator(visitor)类将不会依赖于它可以访问的类。实际上,它可以访问任何可以提供所需信息的课程。
在特定情况下,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 :(得分:1)
1)这是该模式的错误实现吗?
否,这仍然是GoF访问者模式。 IPaymentCalculationsVisitor
访问两种不同方法的能力不会改变模式的性质。因为它结合了两次不同访问的逻辑,所以您可能要考虑SOLID原则。
CalculateFee
和CalculateExtraCharge
分离,因此客户端可以拥有一个而没有另一个。请注意,访问者模式可以允许将新行为添加到IPaymentMethod
类型的层次结构中,而无需修改诸如CreditCard
和Check
之类的子类。通过将访问者界面分为FeeVisitor
和ExtraChargeVisitor
,两者都可以传递到单个访问方法中。
2)我应该始终将我的方法称为“访问”吗?
否,在任何设计模式下,您都可以根据自己的喜好命名方法。最重要的是,代码对您及其领域具有意义。为了与其他开发人员通俗易懂,如果您认为可以澄清代码的意图,则可以考虑使用该模式的已发布术语添加文档。