插件模式 - IoC / DI与否?

时间:2009-02-27 12:47:17

标签: c# .net design-patterns

这是一个非常普遍的问题,不仅适用于这个例子。

假设您有一个在线商店,并且您想要实施优惠券/礼券,但有约束。假设您有20%的优惠券,但这仅适用于过去3周内添加的产品,但不适用于特殊促销中的产品。

我认为有两种解决方法:第一种方法是将您的商店编码为“本地”支持所有疯狂类型的优惠券。这似乎是经典的方式,但它预先意味着很多工作,而且灵活性很小(毕竟,你事先无法知道你需要什么,也许销售部门可能会提出一些非常棒的新促销活动,需要新的优惠券 - 到下周一)。

第二种方式是插件方式:凭证就像插件一样,每张优惠券都有自己的代码。您将购物篮传入凭证,然后凭证本身会检查每个项目是否适用,进行必要的更改并返回更改后的购物车。

我只是想知道,案例2的设计模式是什么?它看起来有点像IoC / DI,但后来又不是因为代金券没有取代任何现有的功能。它更像是一组具有特殊接口的对象(即IVoucher),然后是一个迭代的IVoucher对象队列。这些类型的“操纵器”是否有标准模式(和最佳实践)?

编辑:感谢您的回答。为了澄清这一点,优惠券(或操纵者 - 如上所述,这不仅仅是关于网上商店的问题,而是关于类似的情况)是“重”的对象,即他们有商业逻辑。因此,我可以说,如果客户在2008年1月1日之前注册,则仅在客户在过去6个月内订购了至少100美元时,才适用优惠券,仅适用于X类中的物品,与其他优惠券“叠加”除外对于标记为减少等的物品等等。所以我更关心的是如何保持一个干净的结构,以确保优惠券得到他们需要的所有,以检查他们是否适用,并能够操纵购物车,所以我想知道关于这种情况的标准是什么,这正是访客模式似乎所做的。

3 个答案:

答案 0 :(得分:7)

在这种情况下,您可以使用the strategy patternthe vistor pattern来计算购物篮的价值。

vistor可以使用不同的策略(在这种情况下是折扣券)访问篮子中的每个项目,并使用这些来计算篮子的全部成本。

可以通过某种方式从数据库中检索使用的凭证,并很容易地将其注入访问者。

凭证策略可能如下所示:

public interface IVoucher
{
    decimal CostOf(CartItem cartItem);
}

默认值如下:

public class FullPriceVoucher : IVoucher
{
    public decimal CostOf(CartItem cartItem)
    {
        return cartItem.Cost;   
    }
}

10%的折扣可能是:

public class TenPercentOffVoucher : IVoucher
{
    public decimal CostOf(CartItem cartItem)
    {
        return cartItem.Cost * 0.9m;   
    }
}

然后你可以有一个访客来计算购物车价值:

public class CartValueVisitor
{
    private IVoucher voucher;

    public CartValueVisitor(IVoucher voucher)
    {
        this.voucher = voucher;
    }

    public decimal CostOf(Cart cart)
    {
        return cart.Items.Sum(item => voucher.CostOf(item));
    }
}

您将使用它:

var cart = GetACart();

var fullPriceCartValueVisitor = 
        new CartValueVisitor(new FullPriceVoucher());
var tenPercentOffCartValueVisitor = 
        new CartValueVisitor(new TenPercentOffVoucher());

var fullPrice = fullPriceCartValueVisitor.CostOf(cart);
var tenPercentOffPrice = tenPercentOffCartValueVisitor.CostOf(cart);

这显然一次只能使用一张优惠券,但应该让您了解一般结构。

答案 1 :(得分:5)

以前的答案表明访客和策略模式对我来说听起来不错,尽管在每个购买项目是同一具体类别的对象的典型情况下,访客是过度的。 Visitor的目的是允许在两个(或更多)对象类型上进行动态调度 - 访问对象是一个层次结构的一部分,访问者是另一个层次结构的一部分。但是,如果只有一种对象类型(实现IVoucher的类的具体类型)不同,则只需要常规的旧单一类型虚拟调度。

事实上,我个人根本不会理解任何“模式” - 您自己的描述正是所需要的:创建一个接口,IVoucher和一堆实现该接口的类。您还需要一个工厂方法,该方法接受凭证代码并返回具有适当具体类型的IVoucher对象。

当心非交换凭证!

您提到的IVoucher实施对象队列将针对购买项运行,这意味着可能会使用多个凭证。 在这种情况下,您需要小心 - 应用凭证A,然后凭证B始终具有与应用B然后A相同的效果吗?不幸的是,许多典型的“特别优惠”似乎没有这个属性(例如,如果代金券A给你10美元的折扣,代金券B给你5%的折扣,那么订单肯定很重要。)

快速而肮脏的方法是为每个凭证分配不同的数字“优先级”值,并始终按优先级顺序应用凭证。为了减少“奇怪”的代金券组合使您破产的可能性,将代金券组合限制在代码中指定的某些允许组合可能也是一个好主意。 (这可以像凭证代码列表一样简单。)

答案 2 :(得分:2)

也许是Visitor pattern?不同类型的优惠券是访客,他们访问购物篮并操纵它。

我认为IOC不是解决方案。