2011-10-05 16 views
3

我正在重構一組類,如下所示,它執行一些價格計算。計算是基於許多參數完成的。 的代碼是:重構基於許多參數進行計算的類的最佳設計模式

public interface IParcel { 
    int SourceCode { get; set; } 
    int DestinationCode { get; set; } 
    int weight{get;set;} 
    decimal CalculatePrice(); 
} 

public abstract class GeneralParcel : IParcel { 
    //implementation of inteface properties 
    //these properties set with in SourceCode & DestinationCode 
    //and are used in CalculatePrice() inside classes that inherit from GeneralParcel 
    protected SourceProvinceCode{get; protected set;} 
    protected DestinationProvinceCode{get;protected set;} 

    //private variables we need for calculations 
    private static ReadOnlyDictionary<int, List<int>> _States_neighboureness; 
    private static ReadOnlyCollection<City> _Citieslist; 
    private static ReadOnlyCollection<Province> _Provinceslist; 

    protected ReadOnlyCollection<City> Citieslist {get { return _Citieslist; }} 

    protected ReadOnlyCollection<Province> ProvincesList {get { return _Provinceslist; }} 

    protected ReadOnlyDictionary<int, List<int>> StatesNeighboureness {get {return _States_neighboureness; }} 

    //constructor code that initializes the static variables 

    //implementation is in concrete classes 
    public abstract decimal CalculatePrice(); 
} 

public ExpressParcel : GeneralParcel { 
    public decimal CalculatePrice() { 
     //use of those three static variables in calculations 
     // plus other properties & parameters 
     // for calculating prices 
    } 
} 


public SpecialParcel : GeneralParcel { 
    public decimal CalculatePrice() { 
     //use of those three static variables in calculations 
     // plus other properties & parameters 
     // for calculating prices 
    } 
} 

眼下,代碼使用「策略模式」有效。

我的問題是,那三個靜態屬性,真的不是parcel對象的一部分,它們只需要進行價格計算,那麼建議哪個設計模式或者wrap(重構)呢?

是否有另一個接口,如下所需(&然後將它們內部的靜態屬性包裹起來?甚至使得該靜態類,因爲它基本上只是一些計算),那麼如何將它連接到IParcel? 這樣做,如何實現CalculatePrice()SpecialParcel & ExpressParcel類?

public interface IPriceCalculator { 
    decimal CalculatePrice(); 
} 

編輯:以上只是所有系統的大畫面,也有在評論,我們鐵餅關於他們其他的考慮,我在這裏他們寫一遍清理的東西。

對於所有ParcelType,有BulkDiscount。當客戶發送10個以上的包裹(或任何閾值)時,發送大量帖子,當一個客戶發送超過10個包裹到一個唯一的目的地(只有一個接收者)時,也有特別折扣。現在這種類型的折扣在每個宗地類型的CalculatePrice()中進行管理。即使7Kg以下的包裹也有折扣。

也現在有3 parceltype,我只在這裏顯示2。但我們需要在將來添加其他類型(TNT & DHL支持)。每種類型都有很多服務,客戶可以選擇並付費。例如 ,sms serviceemail service &等等。

+0

請考慮http://codereview.stackexchange.com/。 –

+0

是的,謝謝,將'this'添加到'there' –

回答

1

IPriceCalculator將是單一責任原則的最佳實踐。

但將方法的簽名更改爲decimal CalculatePrice(IParcel parcel); 該方法調用IParcel的CalculatePrice()方法以獲取每個宗地的基價。

+0

是否就像裝飾模式?我的意思是對於每種類型的包裹(現在在項目中有4種類型),我們需要根據每種類型的IParcel提供接口。 ExpressPriceCalculator等。在它們中的每一個或基本抽象中,都是那三個靜態屬性。 –

+0

它可以被稱爲一種裝飾者。 您只需要一個計算器即可計算所有包裹。 – dwonisch

+0

不幸的是每種類型都有自己的屬性和計算規則!例如SpecialParcel大多數在24小時內交付,但對快遞沒有這種限制,所以定價公式差別很大 –

2

您爲給定宗地計算價格的方式是不應該屬於數據對象的責任。

鑑於你告訴我,這裏是我將如何實現,嘗試並考慮未來的考慮:

public interface IParcel { 
    int SourceCode { get; set; } 
    int DesinationCode { get; set; } 
    int Weight { get; set; } 
} 

public class PricingCondition { 
    //some conditions that you care about for calculations, maybe the amount of bulk or the date of the sale 
    //might possibly be just an enum depending on complexity 
} 

public static class PricingConditions { 
    public static readonly PricingCondition FourthOfJulyPricingCondition = new PricingCondition(); 
    public static readonly PricingCondition BulkOrderPricingCondition = new PricingCondition(); 
    //these could alternatively come from a database depending on your requirements 
} 

public interface IParcelBasePriceLookupService { 
    decimal GetBasePrice(IParcel parcel); 
    //probably some sort of caching 
} 

public class ParcelPriceCalculator { 
    IParcelBasePriceLookupService _basePriceLookupService; 

    decimal CalculatePrice(IParcel parcel, IEnumerable<PricingCondition> pricingConditions = new List<PricingCondition>()) { 
     //do some stuff 
    } 
    decimal CalculatePrice(IEnumerable<IParcel> parcels, IEnumerable<PricingCondition> pricingConditions = new List<PricingCondition>()) { 
     //do some stuff, probably a loop calling the first method 
    } 
} 
+0

請閱讀此鏈接,最後我要混淆你的代碼和這[鏈接](http://codereview.stackexchange.com/questions/5201/best-design-pattern-for-refactoring-a-set-這種做計算的基礎上) –

0

像你說的,那些靜態屬性並不是真正的GeneralParcel類的一部分。將它們移動到靜態的「ListsOfThings」類。

然後你可以使用代碼ListsOfThings.ProvincesList

+0

我想到了,但維護代碼呢? 「IPriceCalculator」呢? –

+0

爲什麼會有維修問題?你有名字爲「ListsOfThings」的類中存儲的事物清單。靜態屬性或其他靜態屬性在界面中是沒有意義的。 –

+0

@woni這個項目的許多變化都在前面,所以看起來(似乎只有),這不是好的做法。我覺得這不是最好的做法。 –

3

就個人而言,雖然其他人可能會說包裹不應該知道如何計算自己的運費,但我不同意。您的設計已經通過三種不同的計算標識出有三種不同的計算,所以對於我的(天真的)眼睛來說,物體應該有一種方法是完全合適的,例如, CalculatePrice()

如果你真的想要走那條路,那麼你需要的IParcelPriceCalculator兩個實現(或者無論你怎麼稱呼它),在GeneralParcel一個抽象的工廠方法來創建具體ExpressParcelPriceCalculatorSpecialParcelPriceCalculator類。個人而言,我認爲這是一種矯枉過正的情況,至少由於代碼將與每個GeneralParcel實現緊密耦合。

我會然而考慮作出的CityProvinceCityProvince公共靜態屬性靜態集合分別。這只是更整潔,而且如果我維護代碼,它就是我期望找到它們的地方。 StatesNeighbourliness可能應該進入省,否則它甚至可能證明自己的階級是合理的。

+0

@woni是的,我也更喜歡包裹大多數不知道價格是如何計算的,但重要的是我們正在將此代碼更改爲儘可能可用。商務正在增長,也有要求添加TNT和DHL鏈接(在3個月的時間),所以我們最多添加一些其他類型的包裹。 –

+1

啊,在這種情況下,您可能需要一個「Carrier」類,爲每個運營商_does_實施「IPriceCalculator」方法。所以也許抽象工廠採取'載體'參數可能是一條路。有時間完成一些單元測試,看看哪個感覺最好! –

+0

@John讓我們來寫和測試這三種方法('靜態ListOfThings','SpecialParcelPriceCalculator'&'載體')。 +1''感覺最好!' –

1

我提供的建議將取決於你如何去生成和使用Parcel多形體。我的意思是,我們看不到用什麼標準來確定某些東西是「快速」還是「特殊」,以及這些標準是否與包裹本身的屬性或某些外部因素有關。儘管如此,我認爲你的直覺是將價格計算與Parcel對象本身分開的好方法。正如kmkemp指出的那樣,包裹不應該根據包裹的類型來計算包裹的價格。一個包裹是一個數據傳輸/ POCO類型的對象,至少如您給它的重量,來源等所示。

但是,我不清楚你爲什麼使用這些接口。不要誤會我的意思 - 接口對於解耦和可測試性來說很好,但爲什麼除了抽象基類和抽象方法以外,還有一個parcel接口。就個人而言,事情只是我的資料,我應該這樣做:

public class Parcel 
{ 
    int SourceCode { get; set; } 
    int DestinationCode { get; set; } 
    int weight { get; set; } 
} 

public abstract class GeneralCalculator 
{ 
    //Statics go here, or you can inject them as instance variables 
    //and they make sense here, since this is presumably data for price calculation 
    protected static ReadOnlyDictionary<int, List<int>> _States_neighboureness; 
    protected static ReadOnlyCollection<City> _Citieslist; 
    protected static ReadOnlyCollection<Province> _Provinceslist; 
    //.... etc 

    public abstract Decimal CalculatePrice(Parcel parcel); 
} 

public class ExpressCalculator : GeneralCalculator 
{ 
    public override decimal CalculatePrice(Parcel parcel) 
    { 
     return 0.0M; 
    } 
} 

public class SpecialCalculator : GeneralCalculator 
{ 
    public override decimal CalculatePrice(Parcel parcel) 
    { 
     return 0.0M; 
    } 
} 

但同樣,我不知道怎麼包裹實際上正在處理中。您可能需要對此概念進行某種修改,具體取決於您如何生成並處理這些包裹。例如,如果包裹的種類取決於包裹的屬性值,則可能需要定義一個工廠方法或類,它接受一個包裹並返回計算器的相應實例。

但是,無論如何修改它,我一定會爲您考慮將計算其價格的計劃的定義從計劃中解耦出來而投下我的票。數據存儲和數據處理是不同的問題。我也不會贊成在某個包含全局設置的靜態類,但這是我個人的喜好 - 這種事情很容易獲得一個setter併成爲一個全球變量。

+0

確實是客戶說他/她想使用哪種類型的包裹。順便說一句,每種類型的包裹都有一些更多的屬性和服務,爲了簡單起見,我不會說或寫螞蟻的東西。例如,SpecialParcel有一個選項,如果客戶想要,可以在交付時發送短信(即使在每個Exchange節點交付時),也可以接收電子郵件和更多類似於每種類型的事情。那麼'interface IParcel'的規則是清除這些類型之間只有一些基本屬性。 –

+0

那麼,如果包裹具有多態的真實原因,另一個設計概念將是讓一個價格計算器具有靜態的東西,並揭示價格計算器在抽象基礎宗地類中使用的所有東西。然後重寫DTO類中的地塊之間的差異。例如,將SMS屬性碰到基類並使其返回false,然後在特殊情況下重寫並使其返回true。然後定價計算器將使用此屬性(如果適用),而不關心哪種類型的包裹提供它。 –

相關問題