自定义访客模式实施
Custom visitor pattern implementation
我正在尝试实现一种访问者模式。网络上的大多数示例显示访问者 class 具有 "visit" 方法和该方法的多个重载。在这种情况下,我将我的 "visit" 方法称为 CalculateFee(这是一个语义问题)及其重载。到目前为止一切正常,但现在我需要再次实现访问者以执行另一个方法 "CalculateExtraCharge",因此我添加了另一个名为 CalculateExtraCharge 的方法及其重载。但是现在我有两个问题
1) 这是模式的错误实现吗?
2) 我应该总是调用我的方法 "visit" 吗?
这里是我的代码的概述,我省略了其中的一些部分以仅关注对我的问题重要的部分。
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);
}
}
}
1) Is this a wrong implementation of the pattern?
不,这仍然是 GoF 访客模式。 IPaymentCalculationsVisitor
访问两种不同方法的能力不会改变模式的性质。因为它结合了两个不同访问的逻辑,你可能要考虑 SOLID 原则,但是。
- 这两种方法都是单一职责的一部分吗,也就是说,它们会因为同样的原因而改变,还是一种方法可能会独立于另一种方法而改变?
- 潜在客户是否总是依赖于这两种方法? 接口隔离 将解耦
CalculateFee
和 CalculateExtraCharge
,因此客户可以有一个没有另一个。
请注意,访问者模式可以允许将新行为添加到 IPaymentMethod
类型层次结构中,而无需修改子 类,例如 CreditCard
和 Check
.通过将访问者界面拆分为 FeeVisitor
和 ExtraChargeVisitor
,两者都可以传递到一个访问方法中。
2) Should i always call my method "visit"?
不,在任何设计模式中,您可以随意命名您的方法。最重要的是代码对您及其领域有意义。为了与其他开发人员使用通用词汇表,您可以考虑使用模式的已发布术语添加文档,如果您认为这样可以阐明代码的意图。
2) Should i always call my method "visit"?
不,以更特定于域的方式命名方法。
1) Is this a wrong implementation of the pattern?
看看你的实现,我发现它有点不同。
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);
}
}
面向对象的编程之一是封装,其中对象拥有其数据(不暴露给外界)。
使用访问者模式,我们可以为对象提供额外的功能,而无需将其数据暴露在外部。
因为内部数据没有暴露给对象的外部,访问者需要"visit inside the object",其中对象将能够向访问者提供所需的值而不暴露这些值(不使这些值public).
对于问题的情况,我们可以将 calculator(visitor) 传递给 CreditCard
class,其中 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
}
}
使用这种方法,计算器(访问者)class 将不依赖于它可以访问的 classes。实际上它可以访问任何可以提供所需信息的class。
在 CreditCard
公开数据的特定情况下(有 public 属性 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);
}
}
我正在尝试实现一种访问者模式。网络上的大多数示例显示访问者 class 具有 "visit" 方法和该方法的多个重载。在这种情况下,我将我的 "visit" 方法称为 CalculateFee(这是一个语义问题)及其重载。到目前为止一切正常,但现在我需要再次实现访问者以执行另一个方法 "CalculateExtraCharge",因此我添加了另一个名为 CalculateExtraCharge 的方法及其重载。但是现在我有两个问题
1) 这是模式的错误实现吗?
2) 我应该总是调用我的方法 "visit" 吗?
这里是我的代码的概述,我省略了其中的一些部分以仅关注对我的问题重要的部分。
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);
}
}
}
1) Is this a wrong implementation of the pattern?
不,这仍然是 GoF 访客模式。 IPaymentCalculationsVisitor
访问两种不同方法的能力不会改变模式的性质。因为它结合了两个不同访问的逻辑,你可能要考虑 SOLID 原则,但是。
- 这两种方法都是单一职责的一部分吗,也就是说,它们会因为同样的原因而改变,还是一种方法可能会独立于另一种方法而改变?
- 潜在客户是否总是依赖于这两种方法? 接口隔离 将解耦
CalculateFee
和CalculateExtraCharge
,因此客户可以有一个没有另一个。
请注意,访问者模式可以允许将新行为添加到 IPaymentMethod
类型层次结构中,而无需修改子 类,例如 CreditCard
和 Check
.通过将访问者界面拆分为 FeeVisitor
和 ExtraChargeVisitor
,两者都可以传递到一个访问方法中。
2) Should i always call my method "visit"?
不,在任何设计模式中,您可以随意命名您的方法。最重要的是代码对您及其领域有意义。为了与其他开发人员使用通用词汇表,您可以考虑使用模式的已发布术语添加文档,如果您认为这样可以阐明代码的意图。
2) Should i always call my method "visit"?
不,以更特定于域的方式命名方法。
1) Is this a wrong implementation of the pattern?
看看你的实现,我发现它有点不同。
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);
}
}
面向对象的编程之一是封装,其中对象拥有其数据(不暴露给外界)。
使用访问者模式,我们可以为对象提供额外的功能,而无需将其数据暴露在外部。
因为内部数据没有暴露给对象的外部,访问者需要"visit inside the object",其中对象将能够向访问者提供所需的值而不暴露这些值(不使这些值public).
对于问题的情况,我们可以将 calculator(visitor) 传递给 CreditCard
class,其中 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
}
}
使用这种方法,计算器(访问者)class 将不依赖于它可以访问的 classes。实际上它可以访问任何可以提供所需信息的class。
在 CreditCard
公开数据的特定情况下(有 public 属性 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);
}
}