2

假设定义了两个简单的装饰器:

// decorated object
class Product : IComponent {
    // properties..        
    // IComponent implementation
    public Decimal GetCost() {
        return this.SelectedQuantity * this.PricePerPiece;
    }
}

// decorators
class FixedDiscountDecorator : IComponent {
    IComponent component;
    // IComponent implemantation
    public Decimal GetCost() {
        // ...
    }
    public FixedDiscountDecorator(IComponent product, Decimal discountPercentage) {
        // ...
    }
}

class BuyXGetYFreeDiscountDecorator : IComponent {
    IComponent component;
    // IComponent implemantation
    public Decimal GetCost() {
        // ...
    }
    // X - things to buy
    // Y - things you get free
    public BuyXGetYFreeDiscountDecorator(IComponent product, Int32 X, Int32 Y) {
        // ...
    }
}

这些装饰器具有不同的构造函数签名(参数列表)。我正在寻找一种模式来构建装饰器,就像工厂模式一样。我的意思是我放了一个字符串并得到一个装饰器实例。

因此,我想简单地将一系列装饰器应用于给定产品:

var product = new SimpleProduct {
    Id = Guid.NewGuid(),
    PricePerPiece = 10M,
    SelectedQuantity = 10,
    Title = "simple product"
};

var itemsToApplyTheDiscount = 5;
var itemsYouGetFree = 2;
var discountPercentage = 0.3M;

var discountA = new BuyXGetYFreeDecorator(product, itemsToApplyTheDiscount, itemsYouGetFree);
var discountB = new FixedDiscountDecorator(discountA, discountPercentage);
4

2 回答 2

3

在您的示例中,您确实显示了应用于给定产品的装饰器链,即:

var discountA = new BuyXGetYFreeDecorator(product, itemsToApplyTheDiscount, itemsYouGetFree);
var discountB = new FixedDiscountDecorator(discountA, discountPercentage);

那么问题是,有哪些模式可以用来改变product由指定属性控制的状态,从上面的代码构建?使用您的示例并将我的范围限制为确定product成本:

public interface IComponent
{
    decimal GetCost { get; set; }
}

我会创建一个代表 IComponent 的产品类

public class Product
{
    public IComponent Price { get; set; }
}

除了装饰器类之外,您可能还有一个“默认”实现

public class BasePrice : IComponent
{
    private Decimal _cost;

    public decimal GetCost //as a property maybe use Cost with get; set; in IComponent
    {
        get { return _cost; }
        set { _cost = value; }
    }
}

我喜欢你的两个装饰器类,它们使用 IoC 并从 IComponent 实现成本变化 (GetCost())。

到目前为止,我还没有真正添加任何东西,只是一个基本价格等级。我接下来可能做的是使用抽象类并将特定操作推迟到子类,如模板方法模式中所示。我的具体类将从这个基类继承。创建的具体类将取决于传递给工厂方法模式的操作类型,该类在下面称为 WarrantyProcessFactory。

您提到使用 app.config ...我喜欢枚举,我将使用它来指定要应用于的操作类型product。因此,假设我希望采取与产品保修相关的行动,以便我可以基于此处理产品。

public enum WarrantyAction
{
    RefundProduct = 0,
    ReplaceProduct = 1
}

我将使用保修请求类来代表客户对缺陷产品的赔偿请求

public class WarrantyRequest
{
    public WarrantyAction Action { get; set; }
    public string PaymentTransactionId { get; set; }
    public decimal PricePaid { get; set; }
    public decimal PostageCost { get; set; }
    public long ProductId { get; set; }
    public decimal AmountToRefund { get; set; }
}

最后,我可以实现抽象模板方法,该方法将被代表保修操作枚举的具体类覆盖

public abstract class WarrantyProcessTemplate
{
    protected abstract void GenerateWarrantyTransactionFor(WarrantyRequest warrantyRequest);
    protected abstract void CalculateRefundFor(WarrantyRequest warrantyRequest);

    public void Process(WarrantyRequest warrantyRequest)
    {
        GenerateWarrantyTransactionFor(warrantyRequest);
        CalculateRefundFor(warrantyRequest);
    }
}

类和前两个方法是抽象的,需要子类实现。第三个方法简单地调用两个抽象方法并将 WarrantyRequest 实体作为参数传递。

客户要求退款的情况

public class RefundWarrantyProcess : WarrantyProcessTemplate
{
    protected override void GenerateWarrantyTransactionFor(WarrantyRequest warrantyRequest)
    {
        // Code to determine terms of the warranty and devalutionAmt...
    }

    protected override void CalculateRefundFor(WarrantyRequest warrantyRequest)
    {
        WarrantyRequest.AmountToRefund = warrantyRequest.PricePaid * devalutionAmt;
    }
}

客户需要更换产品的情况

public class ReplaceWarrantyProcess : WarrantyProcessTemplate
{
    protected override void GenerateWarrantyTransactionFor(WarrantyRequest warrantyRequest)
    {
        // Code to generate replacement order
    }

    protected override void CalculateRefundFor(WarrantyRequest warrantyRequest)
    {
        WarrantyRequest.AmountToRefund = warrantyRequest.PostageCost;
    }
}

public static class WarrantyProcessFactory
{
    public static WarrantyProcessTemplate CreateFrom(WarrantyAction warrantyAction)
    {
        switch (warrantyAction)
        {
            case (WarrantyAction.RefundProduct):
                return new RefundWarrantyProcess();
            case (WarrantyAction.ReplaceProduct):
                return new ReplaceWarrantyProcess();
            default:
                throw new ApplicationException(
                     "No Process Template defined for Warranty Action of " +
                                               warrantyAction.ToString());
        }
    }
}

我会考虑的另一种模式是策略方法模式。在这种情况下,Context 类将所有计算推迟到其抽象类或接口引用的“ConcreteStrategy”。我发现 Scott Millet 的书“专业 ASP.NET 设计模式”是一个有用的资源,并且经常返回它。

我愿意接受评论和批评。

于 2013-09-11T00:24:52.050 回答
3

这可以使用 IOC 容器或类似的东西来解决。我脑海中弹出的一些容器是UnityWindsorSimpleInjector。因为我在那里没有经验,所以我会将 IOC 容器留给其他答案。

但是,我真的很想知道您为什么要注入本机值。

看到如何使用该类,将诸如折扣百分比或 x 购买 y 免费项目注入构造函数的注入值感觉很奇怪。

如果用户将 10(百分比)而不是 0.1(十进制)作为折扣参数会怎样?它使模棱两可。另外,如果你在构造函数中添加检查,你就给了这个类另一个责任,违反了 SRP。

我建议添加一个 DTO,例如DiscountPercentValueor BuyXGetYFreeValue。此外,我更喜欢将 discount 的值设置为上下文,或者为它注入一个存储库。否则,有一天您将需要factories处理与折扣相关的 if-else 业务规则。

编辑1:

通常我null只保留构造函数验证作为检查。can be除了认为违规之外的其他验证。

至于存储库的东西,我想像这样的一些接口:

public interface IDiscountPercentageProvider{
    DiscountValue Get();
}

public interface IBuyXGetYFreeValueProvider{
    BuyXGetYFreeValue Get();
}

然后在您的服务类中,您可以使用如下内容:

class FixedDiscountDecorator : IComponent {
    IComponent component;
    // IComponent implemantation
    IDiscountPercentageProvider discountPercentageProvider;
    public Decimal GetCost() {
        DiscountValue discount = discountPercentageProvider.Get();
        // ...
    }
    public FixedDiscountDecorator(IComponent product
        , IDiscountPercentageProvider discountPercentageProvider) {
        // ... just null checks here
    }
}

一开始这可能很复杂。然而,它提供了更好的 API 设计(现在使用装饰器时没有歧义)。使用它,您可以将 a 创建为保护其不变量DiscountValue的类,使其在其他类中使用更安全。

于 2013-09-03T11:31:52.430 回答