2011-11-13 27 views
2

我有一個類優惠券:與非繼承的方法處理在子類中

public abstract class Voucher 
    { 
     public int Id { get; set; } 
     public decimal Value { get; protected set; } 
     public const string SuccessMessage = "Applied"; 
    } 

和子類心願單

public class GiftVoucher : Voucher 
    { 
    } 

和另一個子DiscountVoucher

public class DiscountVoucher : Voucher 
    { 
     public decimal Threshold { get; private set; } 
     public string FailureMessage { get { return "Please spend £{0} to use this discount"; } } 
    } 

你可以看到DiscountVoucher有一些特定的屬性Threshold和FailureMessage,它們分別代表了amou如果用戶沒有花費這筆錢,則需要花費用於獲得折扣和失敗消息。

我的問題是這樣的。我有券對象的集合,什麼我不希望在我的代碼做的是這樣的

if (voucher is DiscountVoucher) 
{ 
    // cast voucher to a DiscountVoucher and then call the specific methods on it 
} 

東西,因爲這不是在所有的維護。同時,我不想將這些具體方法放在憑證抽象類中,因爲它們不適用於所有類型的憑證。有誰知道如何設計這個功能?

+2

您必須對您的收藏執行哪些任務? – foowtf

回答

3

那麼你在這裏得到的是一個戰略模式的版本。我不認爲最終不得不決定是否有一種類型的優惠券或另一種優惠券,但是如果您願意,可以使用界面限制變體的數量 - 優惠券類別。

例如,您最終可能會獲得五個實現名爲「StandardVoucher」的接口和三個名爲「DiscountVoucher」的憑證,但不必處理八個您現在只有兩個的案例。

這些接口可以覆蓋一系列顯示可用方法的憑證,而不用擔心每個憑單實施的細節。

4

在一般情況下:不!

在沒有任何代碼處理特殊情況的情況下處理通用代碼流中的專用場景不起作用。

但是在某些情況下,您可以作弊一點點。您可以在提供默認「無」實現的抽象基類中實現虛擬方法。

可能是一個返回null,0或者什麼都不做的方法。

在這種情況下

public virtual string FailureMessage { get { return string.Empty; } } 

可能是一個合理的實現。

我想你的實現看起來很像template method pattern。那麼對於不適用於某些實現的步驟使用void實現是非常正常的。

+0

問題在於,這打破了基本的OO原則。我們不應該在憑證中擁有不適用於所有憑證的東西。 –

+1

它不會破壞面向對象的原則。從Voucher返回一個空的失敗信息是完全有意義的,這意味着沒有錯誤發生。 –

+0

如果您的原則在實踐中不起作用:使用其他原則。您需要確定通用憑證應該能夠處理的內容並將其放入基類中。這不一定對應於適用於以後爲基類建模的物理對象的操作。 –

3

不,你不能,因爲遍歷更多的通用對象,然後調用特定的方法需要使用多態性在每個子類中都有專用的功能。沒有超類中的方法來覆蓋,你沒有辦法獲得你想要的。

1

我認爲你是對你描述的代碼持懷疑態度。

我首先想到的是,如果DiscountVoucher成員不夠寬泛的Voucher虛擬或抽象的存在,那麼需要一個Voucher作爲參數的函數不應該去碰它們。

因此,要解決這個問題,我說你可以做兩兩件事之一:

首先,你可以虛方法或屬性添加到Voucher,例如

public abstract class Voucher 
{ 
    public int Id { get; set; } 
    public decimal Value { get; protected set; } 
    public const string SuccessMessage = "Applied"; 
    public decimal Threshold { get { return 0.0; } } 
    public string FailureMessage { get { return ""; } } 
} 

其次,您可以添加方法,對每個Voucher做你期望的操作。你把它們組合在一起作爲代金券,所以想想它們的共同之處。如果,例如,GiftVoucherDiscountVoucher都在執行自己的計算以確定它們是否適用於當前的ShoppingCart,則可以使用名爲isValid()Voucher方法來檢測此問題。例如,

public abstract class Voucher 
{ 
    public bool isValid(ShoppingCart sc); 
    public string FailureMessage { get { return "This voucher does not apply"; } } 
    // ... 
} 

public class DiscountVoucher : Voucher 
{ 
    private decimal Threshold; 
    public override bool isValid(ShoppingCart sc) 
    { 
     return (sc.total >= Threshold); 
    } 
    public override string FailureMessage 
    { 
     get { return FormatString("Please spend £{0} to use this discount", Threshold); } 
    } 
1

有些情況下您必須施放。在這裏,我將實現一個通用的錯誤檢查機制:

public abstract class Voucher 
{ 
    public int Id { get; set; } 
    public decimal Value { get; protected set; } 
    public virtual string SuccessMessage { get { return "Applied"; } } 
    public virtual string FailureMessage { get { return String.Empty; } } 
    public virtual bool Ok { get { return true; } } 
} 

public class GiftVoucher : Voucher { } 

public class DiscountVoucher : Voucher 
{ 
    public decimal Threshold { get; private set; } 
    public override string FailureMessage { get { return "Please spend £{0} to use this discount"; } } 
    public override bool Ok { get { return Value >= Threshold; } } 
} 

然後,您可以測試任何類型的憑證的完整性,同時又不鑄造:

if (voucher.Ok) { 
    Console.WriteLine(voucher.SuccessMessage); 
} else { 
    Console.WriteLine(voucher.FailureMessage); 
} 

作爲一般規則,儘量讓對象做自己自己的東西(這裏來測試他們是否可以),而不是從「外部」進行。即使這樣一個事實,GiftVoucher中不會出現任何錯誤,也不需要被「外部世界」所瞭解。

+0

謝謝。這看起來很完美。 –

+0

只是想說雖然Ok的實現是錯誤的。該值指的是優惠券的價值(5英鎊折扣),而閾值指的是在您獲得5英鎊折扣之前需要支付多少費用(例如,您需要花費50英鎊)。所以,這是問題所在。你完全正確地說對象應該自己進行計算,但這意味着我需要給Voucher另一個屬性(用戶花費的金額)。然後,在Ok方法中,我可以比較Threshold,你會同意這種方法嗎?我問的原因是憑證... –

+0

...知道用戶花了多少錢,似乎有點不自然。但另一種選擇是,我們從「外部」決定憑證申請是否正常。我們如何處理這個矛盾? –