如何设计缺乏多重继承?

时间:2011-06-16 21:02:09

标签: oop design-patterns language-agnostic class-design multiple-inheritance

使用接口不起作用,因为我想要一个单独的实现。使用this解决方案将导致大量冗余代码,因为我计划有相当多的子类(组合与继承)。我已经决定特定于问题的设计解决方案是我正在寻找的,我想不出任何优雅。

基本上我希望类具有单独的属性,并且要在设计时将这些属性附加到我选择的任何子类。说,我有班'忍者'。我希望能够制作任意的子类,例如'grayNinja',其中灰色忍者将永远拥有一把剑并投掷星星。然后可能是'redNinja',他将永远拥有一把剑和一个斗篷。显然剑,星星和披肩都有自己的实现 - 这是我不能使用接口的地方。我能找到的最接近的解决方案是decorator pattern,但我不想在运行时使用该功能。最好的解决方案是分支吗?在Black Ninja类构造函数中,我将它传递给剑和投掷星的构造函数? (那些是抽象类)

一段时间没有编码而且阅读没有让我走得太远 - 如果答案很简单,请原谅我。

修改:回答了我自己的问题。直到明天我才能将其标记为“回答”。请告诉我是否有问题,我没有抓到。所有阅读这个问题迫使我做的事情都很棒。学到了很多。

4 个答案:

答案 0 :(得分: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

我知道这个解决方案很复杂,但似乎没有其他办法。

干杯。

答案 1 :(得分:0)

您希望类具有单独的属性。您是否考虑过准确编码?

例如,你想要一个RedNinja,它是一个拥有剑和斗篷的忍者。好的,所以定义Ninja有一个库存,让它可以通过Ninja访问,并通过RedNinja的构造函数传递一个库存。你可以为行为做同样的事情。

答案 2 :(得分: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

答案 3 :(得分:-1)

如果您只是必须具有多重继承,那么脏问题的解决方案就是使用类似dynamic proxies in Java的内容。

但是我想你可能是用C#编程的,这是语言无关的问题,所以这里是语言不可知的答案:查看compositefactory设计模式,这应该会给你一些想法

此外,可能不需要在构造函数中传递所有内容。查看IoC pattern