2011-05-24 34 views
3

我會很感激任何意見在下面的情況下去哪裏。讓我們看看我能否清楚解釋(英語不是我的母語,因此可能會讓人困惑,對不起)。接口和類的繼承。這是一種可避免的模式嗎?

假設我有以下接口:

internal interface IBlah 
{ 
    int Frob(); 
} 

internal interface IBlahOnSteroids: IBlah 
{ 
    double Duh(); 
} 

現在我們有一個Foo類「有」與IBlah對象關係:

public class Foo 
{ 
    IBlah blah; 

    internal Foo(IBlah blah) 
    { 
      this.blah = blah; 
    } 

    public int Frob() 
    { 
      .... 
      return this.blah.Frob(); 
    } 
} 

現在,我們還需要一個FooOnSteroids類與IBlahOnSteroids對象有'有'關係。 問題是,知道IBlahOnSteroids的一部分已經在Foo中實現,如果我們創建了從Foo繼承的FooOnSteroids,會發生什麼?

我們會得到這樣的事情:

public class FooOnSteroids: Foo 
{ 
    IBlahOnSteroids blah; 

    internal FooOnSteroids(IBlahOnSteroids blah) 
     :base(blah) 
    { 
     this.blah = blah; 
    } 

    public double Duh() 
    { 
     return this.blah.Duh(); 
    } 
} 

這是一個推薦的模式?我們將繼承鏈傳遞給同一個'blah'對象,並且在每個「級別」我們將它存儲在私有的 字段中,並且使用'useful'類型。我沒有辦法,我可以看到,我可以在BlahBase中存儲一個受保護的屬性, 將一個常見的IBlah引用暴露給所有降序類,因爲它必須是IBlah類型,對BlahOnSteroids沒有任何用處。這種情況下甚至推薦使用 ?或者我們應該將Foo和FooOnSteroids作爲獨立的類來實現,而且沒有繼承(這會產生代碼重複)?也許它完全可以做到這一點,但它不知何故感覺像一個黑客。是嗎?

使用泛型,這將立即解決問題的選項是不可能的,因爲,是的,我知道它很爛,這個庫必須針對.Net 1.x平臺。

只是實現BlahOnSteroids的選項也是一種可能性,但這意味着根據調用者的不同,如果有任何一個IBlaOnSteroids成員被調用,我們將不得不拋出異常。我不喜歡那樣。

非常感謝您的任何建議!

回答

2

你可以通過提供基礎領域減少重複:

IBlah blah; 
protected IBlah Blah { get { return blah; } } 

,並在子類型轉換(因爲你希望你的blah選擇得到尊重):

public double Duh() { 
    return ((IBlahOnSteroids)Blah).Duh(); 
} 

你也可以在基礎類型上做一些泛型(爲了避免演員),但我不確定這是否值得。但是,請注意,如果基類決定注入一個裝飾器(比如blah(如果裝飾器不提供第二個接口)),這可能會爆炸。

+0

我正在考慮這個確切的建議。但演員讓我感覺很簡單,因爲**我認爲**每個接口只有一個方法的例子只是爲了這個例子,實際情況可能與每個接口暴露許多方法有很大不同,案例我想知道是否攜帶額外的實例參考在整個演出中對整體情況和潛在的過度演員造成真正的區別?當然,通過提供返回演員參考的屬性可以避免演員陣容,演員陣容會更清晰。 – 2011-05-24 16:54:34

+0

我在鑄造中看到的問題是,它已經看起來「醜陋」(沒有任何意圖),它會遍佈整個代碼(是的IFoo和IFooOnSteroids相當多的成員比1) – InBetween 2011-05-24 17:58:10

2

一種替代模式是

public class Foo 
{ 
    protected IBlah Blah { get; private set; } 
    ... 
} 
public class FooOnSteroids : Foo 
{ 
    private new IBlahOnSteroids Blah { get { return (IBlahOnSteroids)base.Blah; } } 
    ... 
} 

然而,這是不是從你的代碼非常不同;如果你不能使用泛型,它們都很好。

+0

+1 - 我沒有想到使用這樣的get訪問器。旁註:您可以通過使用'as'關鍵字而不是C風格轉換來獲得更好的性能。 '私人新的IBlahOnSteroids布拉赫{獲得{返回(base.Blah作爲IBlahOnSteroids); },因爲它繞過了C風格強制轉換的異常邏輯。 (來源:http://www.codeproject.com/KB/cs/csharpcasts.aspx) – 2011-05-24 16:53:42

+0

感謝您的建議! – InBetween 2011-05-24 16:56:04

1

迴應你關於去除Foo和FooOnSteroids之間的繼承關係的問題,我不知道你所有的推理,但我可以嘗試提供一些一般指導。您應該考慮使用繼承,主要是爲您的客戶提供使用FooOnSteroids實例的功能,但只能爲Foo編寫代碼。

所以,如果你在概念上的客戶做到這一點:

Foo foo = new FooOnSteroids(); 
foo.Frob() 

你應該保持繼承。

如果您只是爲了重新使用代碼而創建繼承關係,我建議您考慮重構類以包含提供共享功能的類。繼承不是代碼重用的最佳模式。

+0

是的,我們肯定可以有FooOnSteriods .Frob()。感謝您的建議。 – InBetween 2011-05-24 17:52:10

1

鑑於您不能使用泛型(這可能會或可能不會使您的具體用例更容易),我會親自選擇鑄造Fooblah成員。在C#鑄造相對無痛:

public double Duh() 
{ 
    return (this.blah as IBlahOnSteroids).Duh(); 
} 

C#中的關鍵字as將評估到null如果對象不能被轉換成您所請求的類型。在上面的例子中,如果this.blah不是IBlahOnSteroids的實例,您將獲得NullReferenceException。您可以檢查對象是否是一個類型的,像這樣的實例:

public double Duh() 
{ 
    if (this.blah is IBlahOnSteroids) 
     return (this.blah as IBlahOnSteroids).Duh(); 
    else 
     throw new InvalidTypeException("Blah is not an instance of IBlahOnSteroids"); 
} 

雖然在你原來的例子代碼它不應該是可能blah,因爲它分配給沒有的IBlahOnSteroids實例在構造函數中,它會在編譯時爲你做出斷言。

+0

強制轉換的問題在於它會遍佈整個代碼(IFoo的成員比1多)。感謝您的建議。 – InBetween 2011-05-24 18:01:01