您的解决方案目前似乎还可以,但是,我宁愿建议您制定某种规则策略,以便您的预订并不真正关心如何支付,而是由用例确定规则(您将请注意,这个解决方案实际上在技术上也是基于策略模式的)。
例如,假设您有一个剧院课程,这是您预订门票的课程。在这个 Theatre 类上有以下方法:
public PaymentResult MakeReservation(IPaymentPolicy paymentPolicy, int itemsToBuy)
{
var result = paymentPolicy.Verify(itemsToBuy);
if(result.HasFailingRules)
{
return result;
}
// Do your booking here.
}
这里的剧院对象负责一个决定 - 是否允许根据提供给我的规则进行预订?如果是,则进行预订,否则报告错误。
然后调用者可以根据用例控制规则。例如:
public void MakePaypalReservation(int itemsToBuy)
{
var rulesPolicy = new PaymentRulesPolicy(
new MaxItemsRule(10),
new MaxAmountRule(10000)
);
var theatre = this.repo.Load("Theatre A"); // Load by id
var paymentResult = theatre.MakeReservation(rulesPolicy, itemsToBuy);
// Here you can use the result for logging or return to the GUI or proceed with the next step if no errors are present.
}
public void MakeCashReservation(int itemsToBuy)
{
var rulesPolicy = new PaymentRulesPolicy(
new MaxItemsRule(2),
new MaxAmountRule(100),
new TimeOfDayRule(8, 20) //Can only buy between 8 in the morning at 8 at night as an example.
);
var theatre = this.repo.Load("Theatre A"); // Load by id
var paymentResult = theatre.MakeReservation(rulesPolicy, itemsToBuy);
// Here you can use the result for logging or return to the GUI or proceed with the next step if no errors are present.
}
假设 PaymentRulesPolicy 有一个带有这个签名的构造函数:
public PaymentRulesPolicy(params IRule[] rules);
每个用例都有一个方法。如果您可以使用代金券等其他方式付款,则可以使用一些新规则制定新政策。
当然,您还必须向 Theatre 对象提供预订所需的所有信息。规则策略的 Verify() 方法可能会接受所有这些信息,并将所需的最少信息传递给各个规则。
以下是规则策略的示例:
public class PaymentRulesPolicy
{
private readonly IRule[] rules;
public PaymentRulesPolicy(params IRule[] rules)
{
this.rules = rules;
}
public PaymentResult Verify(int numItemsToBuy, DateTime bookingDate)
{
var result = new PaymentResult();
foreach(var rule in this.rules)
{
result.Append(rule.Verify(numItemsToBuy, bookingDate);
}
return result;
}
}
这已经是一个糟糕的界面,因为无论执行什么检查,所有规则都需要所有信息。如果这失控了,您可以通过在首次构建策略时传递预订信息来改进它:
var rulesPolicy = new PaymentRulesPolicy(
new MaxItemsRule(2, itemsToBuy),
new MaxAmountRule(100, itemsToBuy, costPerItem),
new TimeOfDayRule(8, 20, currentDateTime)
);
归根结底,这种模式的好处是您的所有业务决策都封装在一个类中,这使得维护非常容易。只要看看这些政策的构建,就有希望让您对它们将执行的内容有一个很好的了解。然后,您可以将这些规则组合成一个更大的策略。
这种方法的另一个好处是单元测试。您可以非常轻松地单独测试规则。您甚至可以为规则创建一个工厂,并测试该工厂是否为每个用例创建了正确的策略,等等。
请记住,这只是众多可能的解决方案之一,这个特定的解决方案可能对您的应用程序来说太过分了,或者它可能不适合您和您的团队熟悉的模式。当您尝试继承解决方案时,您可能会发现考虑到您团队的习惯和经验,它已经足够了,甚至更容易理解。