2010-07-30 21 views
7

我有以下情况,我有不同种类的销售算法来计算销售价格。 FixedSaleStrategy不需要basePrice参数,而所有其他策略实现都需要它。有没有避免这个冗余参数的好方法?在应用策略模式时,是否有避免某些子类中未使用的方法参数的好方法?

public abstract class SalesStrategy 
{ 
    public abstract double GetPrice(double basePrice, double saleAmount); 
} 
public class AmountOffSale : SalesStrategy 
{ 
    public override double GetPrice(double basePrice, double salesAmount) 
    { 
     return basePrice - salesAmount; 
    } 
} 
public class FixedPriceSale : SalesStrategy 
{ 
    public override double GetPrice(double basePrice, double salesAmount) 
    { 
     return salesAmount; 
    } 
} 
+2

首先要做的事情是......将参数改为'decimal'而不是'double'。 **从来没有**使用二进制浮点数来处理货币,因为它们不能准确地表示货币值,甚至像'1.1'这样简单的值,舍入误差在处理民族货币时非常重要。参见:http://en.wikipedia.org/wiki/Floating_point#Representable_numbers.2C_conversion_and_rounding – 2010-07-30 23:44:17

回答

6

在战略模式的核心是观念调用代码不知道实现,被调用。

如果你要改变每实现使用你会发现,你没有得到这个模式的全部利益的参数:呼叫者需要知道哪个实现将要被使用以及如何调用它。

我倾向于做的是传递一个包含超级信息集合(类似于PricingInfo)的类,它总是以相同的方式填充(理想情况下集中在代码中),唯一的区别是实现了战略。

做的好处之一是,我可以添加一个属性,这是过去没有有关我的PricingInfo类(如说,systemDiscount),以及作为一个整体的影响,系统不会太大。

+0

对PricingInfo的好评。将基础实体信息(例如从'Product'或'OrderItem')包裹到PricingInfo中,也允许更多类型的实体(运费?)稍后被带入系统。 – 2013-05-04 12:16:00

+0

并且PricingInfo或类似的'answer'接口,也有助于更好地关注您的API设计;到'算法需要知道什么',而不是'我如何从现有实体的字段中破解'。我觉得这有助于清理和澄清我的代码。 – 2013-05-04 12:17:25

0

在我看来,这不太好。我会保持现状。有各种技巧,你可以使用像params(有一个单一的参数double [] priceData)或IDynamicObject。但最干净的只是让一些策略忽略额外的参数。

2

如果您正在使用C#4.0中,你可以逆转的参数,使basePrice可选,像这样:

public abstract class SalesStrategy 
{ 
    public abstract double GetPrice(double saleAmount, double basePrice = 0d); 
} 

public class AmountOffSale : SalesStrategy 
{ 
    public override double GetPrice(double salesAmount, double basePrice) 
    { 
     return basePrice - salesAmount; 
    } 
} 

public class FixedPriceSale : SalesStrategy 
{ 
    public override double GetPrice(double salesAmount, double basePrice = 0d) 
    { 
     return salesAmount; 
    } 
} 

含义可以做以下...

FixedPriceSale fixedPrice = new FixedPriceSale(); 
... 
fixedPrice.GetPrice(salesAmount); 

注这AmountOffSalebasePrice参数可选的,这意味着以下不会编译:

AmountOffSale amountOffSale = new AmountOffSale(); 
... 
// No overload for method 'GetPrice' takes 1 arguments 
amountOffSale.GetPrice(salesAmount); 
+0

我刚刚输入了相同的回应;然后注意到你的回复,因为我正在验证代码:-)。 – Jess 2010-08-05 15:12:42

5

不,这是不是一个多余的参数;使用SalesStrategy的代码不应该知道它正在使用哪个具体类,所以方法签名在所有派生类中必须相同。

0

从接口中删除不相关参数的一个好方法是将这些参数从子类中传递给构造函数。所以,为您设计的替代方案是:

public interface SalesStrategy 
    { 
     double CalculatePrice(double basePrice); 
    } 

public class FixedPriceSale : SalesStrategy 
    { 
     public double CalculatePrice(double basePrice) 
     { 
      return basePrice; 
     } 
    } 

public class AmountOffSale : SalesStrategy 
    { 
     public double SalesAmount { get; set; } 

     public AmountOffSale(double salesAmount) 
     { 
      this.SalesAmount = salesAmount; 
     } 

     public double CalculatePrice(double basePrice) 
     { 
      return basePrice - SalesAmount; 
     } 
    } 

在这种结构不与子类特定的数据污染的接口。

+2

我认为这种方法的问题是,在某些时候,某些事情需要创建一个'SalesStrategy'子类和**,**事情需要知道basePrice。因此,这有效地将方法调用中的额外参数移至工厂方法。仍然会有一个地方,这个参数没有被使用。 – 2010-07-31 01:31:14

0

另一种替代方法是使用一个参数对象或者Dictionary<string, object>。这样,您可以合并每种方法的参数数量,并在未来需求发生变化时为其他参数留出空间。

其中一个缺点是,Dictionary<string, object>可以使您的代码中的参数更难跟踪,其中参数对象只包含您可以在代码中查看的所有属性。

+0

字典在结构简单且定义明确的情况下(例如定价)并不是很好的设计,而且效率也不高。它们确实具有诸如复杂子模块的配置或选项等应用程序或非常开放的可扩展性 - 但最好在初始化/配置时使用,而不是每次计算。 – 2013-05-04 12:22:19

+0

我同意 - 这是关于使方法签名复杂,高效的代码,可读性等之间的权衡。 – 2013-05-05 17:36:36

相关问题