2010-07-30 22 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

相關問題