Design patterns “嵌套”/“组合战略模式”?

Design patterns “嵌套”/“组合战略模式”?,design-patterns,encapsulation,strategy-pattern,Design Patterns,Encapsulation,Strategy Pattern,我知道标题很奇怪,所以让我试着做一些基本的设置 我有一个叫做StyleBundle的对象。基于两件事,样式包的持续时间和样式包的类型Unlimited或PerStyle将决定样式包的总体价格。下面是StyleBundle的一个快速剪辑: public class StyleBundle { public decimal Price {get; set;} public StyleType Type {get; set;} //STyleType is a simple enum,

我知道标题很奇怪,所以让我试着做一些基本的设置

我有一个叫做StyleBundle的对象。基于两件事,样式包的持续时间和样式包的类型Unlimited或PerStyle将决定样式包的总体价格。下面是StyleBundle的一个快速剪辑:

public class StyleBundle
{
    public decimal Price {get; set;}
    public StyleType Type {get; set;} //STyleType is a simple enum, either "Unlimited" or "PerStyle"
    public Duration Duration {get; set;}
}
这是持续时间。基本上,它有一个名为DurationType的枚举,可以是DurationType.OneYear、DurationType.TwoYear等值

public class Duration
{
    public Duration(TimeSpan timeSpan, string name, DurationType type)
    {
        this.TimeSpan = timeSpan;
        this.Name = name;
        this.Type = type;
    }

    public TimeSpan TimeSpan { get; set; }
    public string Name { get; set; }
    public DurationType Type { get; set; }
}
在我的StyleBundle类中,我将持续时间传递给一个名为StyleBundlePricingStrategy的策略工厂。这是一节课:

public class StyleBundlePricingFactory
{
     public static IPricingStrategy GetPricing(Duration duration)
     {
         if (duration.Type == DurationType.OneYear) { return new OneYearPricingStrategy(); }
         if (duration.Type == DurationType.TwoYear) { return new TwoYearPricingStrategy(); }
         etc...
         etc...
     }
 }
返回的类实现IPricingStrategy接口:

public interface IPricingStrategy
{
    decimal GetPriceFor(StyleBundle aStyleBundle);
}
这就是样式包的价格。每个策略类封装了如何检索给定DurationType的价格。以下是OneYearPricingStrategy类的示例:

public class OneYearPricingStrategy : IPricingStrategy
{
    public decimal GetPriceFor(StyleBundle aStyleBundle)
    {
        if (aStyleBundle.StylePricingType == StylePricingType.PerStyle)
        {
            return aStyleBundle.Products.Count() * 2500m;
        }
        else
        {
            return  50000m;
        }
    }
}
if (aStyleBundle.StylePricingType == StylePricingType.PerStyle)
好的,基本的策略设置

让我恼火的是,如果你看一下OneYearPricingStrategy类中的这行代码:

public class OneYearPricingStrategy : IPricingStrategy
{
    public decimal GetPriceFor(StyleBundle aStyleBundle)
    {
        if (aStyleBundle.StylePricingType == StylePricingType.PerStyle)
        {
            return aStyleBundle.Products.Count() * 2500m;
        }
        else
        {
            return  50000m;
        }
    }
}
if (aStyleBundle.StylePricingType == StylePricingType.PerStyle)
您将看到,我仍然必须在Strategy类中使用条件来说明StyleBundle类型。样式包的类型将影响价格的计算方式

对我来说,这是一个糟糕的设计,对于我写的每一个策略类,一年策略,两年策略,等等。。。StylePricingType条件被复制并粘贴到所有策略类

这不好,b/c当我必须添加新的StylePricingType时会发生什么?我必须回到每一个PricingStrategy类并更新代码,所以整个SRP和其他东西都会被删除

我需要的是一种实现某种模式的方法,它允许我将两种策略(持续时间和StyleBundleType)结合起来,并允许规则在一个地方存在。。。不要散布在代码中

当您为一个策略实施时,很容易理解策略模式,但这是两个策略的组合,我知道我现在编写的方法不是一个好的实践,也不能实现我想要的

也许这是错误的模式

任何指点都将不胜感激

谢谢, 迈克

编辑:

作为对Garret回答的回应,我想提供一些更详细的信息,说明我最初是如何获得战略模式的。我并没有首先将实现硬塞进策略中,而是看到了我认为是代码的味道,并决定策略模式可以帮助我

最初,StyleBundle.Price属性的外观如下所示:

public decimal Price
{
    get
    {
        if (this.StylePricingType == StylePricingType.PerStyle)
        {
            if (this.Duration.Type == DurationType.ThreeDays)
            {
                _price = 1500m;
            }
            else if (this.Duration.Type == DurationType.OneYear)
            {
                _price = 2500m;
            }
            else if (this.Duration.Type == DurationType.TwoYears)
            {
                _price = 2000m;
            }
            else if (this.Duration.Type == DurationType.ThreeYears)
            {
                _price = 1650m;
            }
        }
        else if (this.StylePricingType == StylePricingType.Unlimited)
        {
            if (this.Duration.Type == DurationType.ThreeDays)
            {
                throw new Exception("You can not have a StyleBundle of type Unlimited for a Duration of three days.");
            }
            else if (this.Duration.Type == DurationType.OneYear)
            {
                _price = 50000m;
            }
            else if (this.Duration.Type == DurationType.TwoYears)
            {
                _price = 40000m;
            }
            else if (this.Duration.Type == DurationType.ThreeYears)
            {
                _price = 33500m;
            }
        }
        else
        {
            throw new Exception("Illegal StylePricingType passed to Product.");
        }
        return _price;
    }
    private set
    {
        _price = value;
    }
}
public decimal Price
    {
        get
        {
            return _pricingStrategy.GetPriceFor(this);
        }
        private set
        {
            _price = value;
        }
    }
我看到,任何时候我要添加另一个持续时间类型,我都需要进入StyleBundle并更改代码。。。对我来说,这似乎是一个激励原则,足以寻求更好的解决方案

现在,将Strategy设计模式应用于此问题后,我的StyleBundle.Price属性如下所示:

public decimal Price
{
    get
    {
        if (this.StylePricingType == StylePricingType.PerStyle)
        {
            if (this.Duration.Type == DurationType.ThreeDays)
            {
                _price = 1500m;
            }
            else if (this.Duration.Type == DurationType.OneYear)
            {
                _price = 2500m;
            }
            else if (this.Duration.Type == DurationType.TwoYears)
            {
                _price = 2000m;
            }
            else if (this.Duration.Type == DurationType.ThreeYears)
            {
                _price = 1650m;
            }
        }
        else if (this.StylePricingType == StylePricingType.Unlimited)
        {
            if (this.Duration.Type == DurationType.ThreeDays)
            {
                throw new Exception("You can not have a StyleBundle of type Unlimited for a Duration of three days.");
            }
            else if (this.Duration.Type == DurationType.OneYear)
            {
                _price = 50000m;
            }
            else if (this.Duration.Type == DurationType.TwoYears)
            {
                _price = 40000m;
            }
            else if (this.Duration.Type == DurationType.ThreeYears)
            {
                _price = 33500m;
            }
        }
        else
        {
            throw new Exception("Illegal StylePricingType passed to Product.");
        }
        return _price;
    }
    private set
    {
        _price = value;
    }
}
public decimal Price
    {
        get
        {
            return _pricingStrategy.GetPriceFor(this);
        }
        private set
        {
            _price = value;
        }
    }

其中_pricingStrategy是IPricingStrategy,通过调用StyleBundle的构造函数中的StyleBundlePricingFactory.GetPricingduration类来决定要更新哪个实现者。

在编写代码之前,不要尝试思考模式。代码然后重构。有时,您会重构为一个模式

策略模式通常是为希望为类委派行为逻辑时保留的。例如,如果我有一个类棋手,那么大师级棋手实施棋子策略,新手实施棋子策略将是改变我的棋手行为策略的好方法,而棋手移动棋子的界面将不必改变

在你的例子中,你只是有数据,而不是复杂的价格计算策略,所以我会寻找一个合适的数据结构。Durations X PricingStyles上的双嵌套HashMap可以正常工作。例如:

Price calculatePrice(Duration d, PricingStyle s) {
    return map.get(d).get(s);
}

PS:当谈到好的设计时,任何花费较少代码的东西通常都是赢家。

在编写代码之前,不要试图去想一个模式。代码然后重构。有时,您会重构为一个模式

策略模式通常是为希望为类委派行为逻辑时保留的。例如,如果我有一个类棋手,那么大师级棋手实施棋子策略,新手实施棋子策略将是改变我的棋手行为策略的好方法,而棋手移动棋子的界面将不必改变

在你的例子中,你只是有数据,而不是复杂的价格计算策略,所以我会寻找一个合适的数据结构。Durations X PricingStyles上的双嵌套HashMap可以正常工作。例如:

Price calculatePrice(Duration d, PricingStyle s) {
    return map.get(d).get(s);
}

PS:说到好的设计,只要代码少,通常都是赢家。

Garret,请看我的编辑。我最初的代码很糟糕,这里策略模式的应用对我来说似乎很有意义。Garret,请查看我的编辑。我最初的代码很糟糕 ,在这里应用策略模式对我来说似乎很有意义。