3

使用接口將無法正常工作,因爲我需要單個實現。使用this解決方案會以很多冗餘代碼結束,因爲我打算有相當多的子類(組合vs繼承)。我已經決定,我正在尋找一個針對具體問題的設計解決方案,我想不出任何優雅。如何設計缺乏多重繼承?

基本上我希望類具有單獨的屬性,並在設計時將這些屬性附加到我選擇的任何子類。說,我有班'忍者'。我希望能夠製作任意的子類,比如灰色忍者在那裏總會有一把劍和一顆星星。然後可能是'redNinja',他將永遠有一把劍和一個斗篷。很明顯,劍,星星和斗篷都會有自己的實現 - 這是我無法使用接口的地方。我能找到的最接近的解決方案是decorator pattern,但我不希望在運行時發現該功能。最好的解決方案是什麼?在黑色忍者類構造函數裏面,我將它傳遞給劍和throwingStar的構造函數? (那些是抽象類)

有一段時間沒有編碼,閱讀沒有得到太多 - 如果答案很簡單,請原諒我。

編輯:回答了我自己的問題。直到明天,我都不能將它標記爲「答案」。請讓我知道是否有問題,我沒有抓到。所有閱讀這個問題迫使我做的很棒。學到了很多。

+2

我不清楚你爲什麼不喜歡構圖。 – Beta 2011-06-16 21:17:39

+0

(+1)因爲有些O.O.語言支持單一繼承,並且根本沒有接口。 – umlcat 2011-06-16 21:32:41

+1

@Beta - 組合需要您每次都重新鍵入方法。我計劃不僅要有很多的屬性,還要有很多的子類。冗餘代碼看起來非常混亂和不必要。 – Phil 2011-06-16 21:46:27

回答

0

好吧,所以mix-ins through extension methods將成爲我的首選路線。我無法弄清楚如何在vb.net中使用動態代理(似乎需要帶有大量文檔的庫,這些文檔沒有包含我需要的特定文檔)。與使用擴展方法相比,動態代理似乎也有點比解決方案更髒。如果前兩者不起作用,組成應該是我默認的。

所以擴展方法的一個問題是,如果你想保存變量,代碼會變得有點骯髒。雖然不多。另一個問題是,所有的擴展方法都必須在模塊中定義,所以代碼可能看起來有點愚蠢到一個新的眼睛。我將通過在相同文件中使用相應的擴展方法定義我的接口和模塊來解決此問題。

最後,這裏是一些示例vb.net代碼,如果你不想看到一個完整的示例通過鏈接。

Imports System.Runtime.CompilerServices 'for extension methods 

Public Interface ISword 
End Interface 
Public Interface IThrowingStar 
End Interface 

Module ExtensionMethods 

    <Extension()> 
    Public Sub swingSword(ByVal hasASword As ISword) 
     Console.WriteLine("Sword has been swung") 
    End Sub 

    <Extension()> 
    Public Sub throwStar(ByVal hasAStar As IThrowingStar) 
     Console.WriteLine("Star has been thrown") 
    End Sub 

End Module 

Public Class RedNinja 
    Inherits Ninja 
    Implements IThrowingStar, ISword 

    Public Sub New() 
    End Sub 

End Class 

Public MustInherit Class Ninja 

    private curHealth as Integer 

    Public Sub New() 
     curHealth = 100 
    End Sub 

    Public Function getHP() As Integer 
     Return curHealth 
    End Function 

End Class 

Module Module1 

    Sub main() 

     Console.WriteLine("Type any character to continue.") 
     Console.ReadKey() 

     Dim a As New RedNinja 
     a.swingSword() 'prints "Sword has been swung" 
     a.throwStar() 'prints "Star has been thrown" 

     Console.WriteLine("End of program - Type any key to exit") 
     Console.ReadKey() 

    End Sub 
End Module 
0

我做過一次類似的應用程序。使用早期的「C++」編譯器,它完全支持單一繼承和無接口。

// base class for all ninjas 
public class Ninja { 

    // default constructor 
    public Ninja() { ... } 

    // default destructor 
    public ~Ninja() { ... } 
} // class 

public class StarNinja: public Ninja { 

    // default constructor 
    public StarNinja() { ... } 

    // default destructor 
    public ~StarNinja() { ... } 

    public void throwStars() { ... } 
} // class 

public class KatannaNinja: public Ninja { 

    // default constructor 
    public KatannaNinja() { ... } 

    // default destructor 
    public ~KatannaNinja() { ... } 

    public void useKatanna() { ... } 
} // class 

public class InvisibleNinja: public Ninja { 

    // default constructor 
    public InvisibleNinja() { ... } 

    // default destructor 
    public ~InvisibleNinja() { ... } 

    public void becomeVisible() { ... } 
    public void becomeInvisible() { ... } 
} // class 

public class FlyNinja: public Ninja { 

    // default constructor 
    public FlyNinja() { ... } 

    // default destructor 
    public ~FlyNinja() { ... } 

    public void fly() { ... } 
    public void land() { ... } 
} // class 

public class InvincibleNinja: public Ninja { 

    // default constructor 
    public InvincibleNinja() { ... } 

    // default destructor 
    public ~InvincibleNinja() { ... } 

    public void turnToStone() { ... } 
    public void turnToHuman() { ... } 
} // class 

// --> this doesn't need to have the same superclass, 
// --> but, it helps 
public class SuperNinja: public Ninja { 

    StarNinja* LeftArm; 
    InvincibleNinja* RightArm; 
    FlyNinja* LeftLeg; 
    KatannaNinja* RightLeg; 
    InvisibleNinja* Body; 

    // default constructor 
    public SuperNinja() { 
    // -> there is no rule to call composed classes, 
    LeftArm = new StarNinja(); 
    RightArm = new InvincibleNinja(); 
    LeftLeg = new FlyNinja(); 
    RightLeg = new KatannaNinja(); 
    Body = new InvisibleNinja(); 
    } 

    // default destructor 
    public ~SuperNinja() { 
    // -> there is no rule to call composed classes 
    delete LeftArm(); 
    delete RightArm(); 
    delete LeftLeg(); 
    delete RightLeg(); 
    delete Body(); 
    } 

    // --> add all public methods from peers, 
    // --> to main class 
    public void throwStars() { LeftArm->throwStars(); } 
    public void useKatanna() { RightLeg->useKatanna(); } 
    public void becomeVisible() { Body->becomeVisible() } 
    public void becomeInvisible() { Body->becomeInvisible() } 
    public void fly() { LeftLeg->fly() } 
    public void land() { LeftLeg->land() } 
    public void turnToStone() { RightArm->turnToStone(); } 
    public void turnToHuman() { RightArm->turnToHuman(); } 
} // class 

我很害怕,最接近的例子是構圖設計模式。爲了變得更類似於繼承,我創建了一個所有組合類共享的通用基類,並且創建了一個主類,它將成爲多重繼承的結果,它具有組件所有公共方法的副本類。

如果你想使用接口,爲了強制這個主類有所有重要的方法,然後做一個匹配每個組成類的接口,並在主類中實現。

public interface INinja { 
    public void NinjaScream() { ... } 
} // class 

public interface IStarNinja { 
    void throwStars(); 
} // class 

public interface IKatannaNinja { 
    void useKatanna(); 
} // class 

public interface IInvisibleNinja { 
    void becomeVisible(); 
    void becomeInvisible(); 
} // class 

public interface CFlyNinja { 
    void fly(); 
    void land(); 
} // class 

public interface IInvincibleNinja { 
    void turnToStone() { ... } 
    void turnToHuman() { ... } 
} // class 

// base class for all ninjas 
public class CNinja: public INinja { 

    // default constructor 
    public CNinja() { ... } 

    // default destructor 
    public ~CNinja() { ... } 

    public void NinjaScream() { ... } 
} // class 

public class CStarNinja: public CNinja, INinja { 

    // default constructor 
    public CStarNinja() { ... } 

    // default destructor 
    public ~CStarNinja() { ... } 

    public void NinjaScream() { ... } 
    public void throwStars() { ... } 
} // class 

public class CKatannaNinja: public CNinja, IKatannaNinja { 

    // default constructor 
    public CKatannaNinja() { ... } 

    // default destructor 
    public ~CKatannaNinja() { ... } 

    public void NinjaScream() { ... } 
    public void useKatanna() { ... } 
} // class 

public class CInvisibleNinja: public CNinja, IInvisibleNinja { 

    // default constructor 
    public CInvisibleNinja() { ... } 

    // default destructor 
    public ~CInvisibleNinja() { ... } 

    public void becomeVisible() { ... } 
    public void becomeInvisible() { ... } 
} // class 

public class CFlyNinja: public CNinja, IFlyNinja { 

    // default constructor 
    public CFlyNinja() { ... } 

    // default destructor 
    public ~CFlyNinja() { ... } 

    public void fly() { ... } 
    public void land() { ... } 
} // class 

public class CInvincibleNinja: public CNinja, IInvincibleNinja { 

    // default constructor 
    public CInvincibleNinja() { ... } 

    // default destructor 
    public ~CInvincibleNinja() { ... } 

    public void turnToStone() { ... } 
    public void turnToHuman() { ... } 
} // class 

// --> this doesn't need to have the same superclass, 
// --> but, it helps 
public class CSuperNinja: public CNinja, 
    IKatannaNinja, 
    IInvisibleNinja, 
    IFlyNinja, 
    IInvincibleNinja 
{ 

    CStarNinja* LeftArm; 
    CInvincibleNinja* RightArm; 
    CFlyNinja* LeftLeg; 
    CKatannaNinja* RightLeg; 
    CInvisibleNinja* Body; 

    // default constructor 
    public CSuperNinja() { 
    // -> there is no rule to call composed classes 
    LeftArm = new CStarNinja(); 
    RightArm = new CInvincibleNinja(); 
    LeftLeg = new CFlyNinja(); 
    RightLeg = new CKatannaNinja(); 
    Body = new CInvisibleNinja(); 
    } 

    // default destructor 
    public ~SuperNinja() { 
    // -> there is no rule to call composed classes 
    delete LeftArm(); 
    delete RightArm(); 
    delete LeftLeg(); 
    delete RightLeg(); 
    delete Body(); 
    } 

    // --> add all public methods from peers, 
    // --> to main class 
    public void throwStars() { LeftArm->throwStars(); } 
    public void useKatanna() { RightLeg->useKatanna(); } 
    public void becomeVisible() { Body->becomeVisible() } 
    public void becomeInvisible() { Body->becomeInvisible() } 
    public void fly() { LeftLeg->fly() } 
    public void land() { LeftLeg->land() } 
    public void turnToStone() { RightArm->turnToStone(); } 
    public void turnToHuman() { RightArm->turnToHuman(); } 
} // class 

我知道這個解決方案很複雜,但似乎沒有其他辦法。

乾杯。

+0

我熟悉C++,但不熟悉這種語言。我認爲「界面」在這種語言中具有特定的含義,並且你所做的確實看起來像是對我的多重繼承。 – Beta 2011-06-16 22:47:12

+0

@Beta看起來像C#,你可以繼承多個接口 – Raynos 2011-06-16 22:59:04

+0

@Beta和@Raynos。它的僞代碼,不是一個真正的語言,但它的支持類似於C++。第一個例子,C++只有類。第二個例子,我使用支持接口的C++變體(不是C#)。 – umlcat 2011-06-16 23:08:33

0

您希望類具有不同的屬性。你有沒有考慮過編碼?

例如,你需要一個RedNinja,它是一個具有劍和斗篷的忍者。好的,定義忍者有一個庫存,通過忍者訪問,並通過RedNinja的構造函數傳入庫存。你可以爲行爲做同樣的事情。

+0

您是否在技術意義上使用「行爲」?你如何獲得'紅忍者'來拉劍?它有一個'drawSword'方法嗎? – Beta 2011-06-16 22:25:32

+0

有一個drawSword()方法的另一種方法是容納一個行爲對象,其行爲是繪製一把劍。類似於對象的集合,它可以包含在一組行爲中。您可以使用裝飾器來輕鬆檢查canDrawSword()和drawSword()。 – 2011-06-17 00:29:55

+0

菲爾沒有說他不想使用裝飾模式? – Beta 2011-06-17 00:50:38

-1

骯髒的解決方案,如果你只是必須有多重繼承,正在使用類似dynamic proxies in Java

但我想你可能是用C#編程的,這是語言不可知的問題,所以這裏是語言不可知的答案:檢查compositefactory設計模式,應該給你一些想法。

此外,可能不需要在構造函數中傳遞所有內容。檢出IoC pattern

+0

'java中的動態代理'是一個很好的閱讀。如果vb.NET提供了這個功能,你會不會知道?如果這是最好的解決方案,我會查找它。 – Phil 2011-06-17 13:32:34

+0

嗯,事實證明,C#有類似的東西(TransparentProxy),我看到有幾個庫構建它使它更像Java中的動態代理,但我對.NET的知識不夠,無法給你更具體的東西。 – Domchi 2011-07-22 22:12:01