首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >创造装饰师。设计模式

创造装饰师。设计模式
EN

Stack Overflow用户
提问于 2013-09-03 10:24:44
回答 2查看 638关注 0票数 2

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

代码语言:javascript
运行
复制
// 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) {
        // ...
    }
}

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

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

代码语言:javascript
运行
复制
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);
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-09-03 11:31:52

可以使用IOC容器或类似的方法来解决这个问题。我脑海中浮现的一些容器是UnityWindsorSimpleInjector。我将把国际奥委会的容器留给其他的答案,因为我在那里没有经验。

但是,我真的很想知道为什么要注入一个本地值。

考虑到类将如何使用,在构造函数中注入诸如折扣百分比或x购买y空闲项这样的注入值会让人感到奇怪。

如果用户将10 (作为百分比)而不是0.1 (作为小数点)作为折扣参数怎么办?它会造成歧义。此外,如果在构造函数中添加检查,则会将另一个责任交给类,这违反了SRP。

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

EDIT1:

通常,我只将构造函数验证保留为null检查。除该can be以外的其他验证都被认为是违反的。

至于存储库方面,我设想了如下一些接口:

代码语言:javascript
运行
复制
public interface IDiscountPercentageProvider{
    DiscountValue Get();
}

public interface IBuyXGetYFreeValueProvider{
    BuyXGetYFreeValue Get();
}

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

代码语言:javascript
运行
复制
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设计(现在使用装饰器时没有歧义)。使用它,您可以创建一个DiscountValue作为一个保护它的不变量类,这样在其他类中使用它就更安全了。

票数 3
EN

Stack Overflow用户

发布于 2013-09-11 00:24:52

在您的示例中,您确实展示了应用于给定产品的一系列装饰器,即:

代码语言:javascript
运行
复制
var discountA = new BuyXGetYFreeDecorator(product, itemsToApplyTheDiscount, itemsYouGetFree);
var discountB = new FixedDiscountDecorator(discountA, discountPercentage);

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

代码语言:javascript
运行
复制
public interface IComponent
{
    decimal GetCost { get; set; }
}

我将创建一个表示IComponent的产品类

代码语言:javascript
运行
复制
public class Product
{
    public IComponent Price { get; set; }
}

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

代码语言:javascript
运行
复制
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())的两个装饰类。

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

你提到用app.config ..。我喜欢枚举,我将使用枚举来指定要应用于product的操作类型。因此,假设我想让行动与产品的保修相联系,这样我就可以在此基础上处理产品。

代码语言:javascript
运行
复制
public enum WarrantyAction
{
    RefundProduct = 0,
    ReplaceProduct = 1
}

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

代码语言:javascript
运行
复制
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; }
}

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

代码语言:javascript
运行
复制
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实体作为参数传递。

客户要求退款的案例

代码语言:javascript
运行
复制
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;
    }
}

客户想要替换产品的情况

代码语言:javascript
运行
复制
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());
        }
    }
}

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

我愿意接受评论和批评。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18589866

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档