如何设计避免多重继承的缺失?

发布于 2024-11-15 20:19:12 字数 745 浏览 3 评论 0原文

使用接口是行不通的,因为我想要一个单一的实现。使用这个解决方案将导致大量冗余代码,因为我计划拥有相当多的子类(组合与继承)。我决定针对特定问题的设计解决方案就是我正在寻找的,但我想不出任何优雅的东西。

基本上,我希望类具有单独的属性,并在设计时将这些属性附加到我选择的任何子类。比如说,我有“忍者”课程。我希望能够创建任意子类,例如“grayNinja”,其中灰色忍者将始终拥有一把剑和飞镖。然后可能是“redNinja”,他总是拥有一把剑和一件斗篷。显然,剑、星星和斗篷都有自己的实现 - 这是我无法使用接口的地方。我能找到的最接近的解决方案是 装饰器模式,但我不希望该功能位于运行时。最好的解决方案是它的一个分支吗?在 Black Ninja 类构造函数内部的哪个位置,我将它传递给 Sword 和 throwingStar 的构造函数? (那些是抽象类)

已经有一段时间没有编码了,阅读并没有让我走得太远——如果答案很简单,请原谅我。

编辑:回答了我自己的问题。直到明天我才能将其标记为“答案”。如果有我没有发现的问题,请告诉我。阅读这个问题迫使我做的所有事情都很棒。学到了不少东西。

Using interfaces won't work because I want a single implementation. Using this solution would end in a lot of redundant code because I plan on having quite a few sub classes (composition vs inheritance). I've decided that a problem-specific design solution is what I'm looking for, and I can't think of anything elegant.

Basically I want classes to have separate properties, and for those properties to be attached at design time to any sub class I choose. Say, I have class 'ninja'. I would like to be able to make arbitrary sub classes such as 'grayNinja' where a gray ninja will always have a sword and throwing stars. Then possibly 'redNinja' who will always have a sword and a cape. Obviously swords, stars, and capes will each have their own implementation - and this is where I can't use interfaces. The closest solution I could find was the decorator pattern, but I don't want that functionality at runtime. Is the best solution an offshoot of that? Where inside the Black Ninja class constructor, I pass it through the constructors of sword and throwingStar? (those being abstract classes)

haven't coded in a while and reading hasn't gotten me too far - forgive me if the answer is simple.

Edit: Answered my own question. I can't mark it as 'answer' until tomorrow. Please let me know if there's a problem with it that I didn't catch. All the reading this problem forced me to do has been awesome. Learned quite a bit.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

蓬勃野心 2024-11-22 20:19:12

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

例如,您想要一个 RedNinja,即一个拥有剑和斗篷的忍者。好的,定义 Ninja 拥有一个清单,使其可通过 Ninja 访问,并通过 RedNinja 的构造函数传入一个清单。你可以对行为做同样的事情。

You want classes to have separate properties. Have you considered coding exactly that?

For example, you want a RedNinja that is-a Ninja that has-a sword and cape. Okay, so define Ninja to have an inventory, make it accessible through Ninja, and pass in an inventory through RedNinja's constructor. You can do the same thing for behaviors.

梦途 2024-11-22 20:19:12

我曾经做过一个类似的应用程序。早期的“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

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

干杯。

I've done once a similar app. with a earlier "C++" compiler that supported only single inheritance and no interfaces, at all.

// 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

Im afraid, that the most close example is the composition design pattern. In order, to become more similar to inheritance, I make a generic base class that all composite classes share, and I make a main class that will be the result of the multiple inheritance, that has a copy of all the public methods of the component classes.

If you want to use interfaces, to enforce that main class have all important methods,
then make an interface that matches each composing class, and implemented in the main 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

I know this solution is complex, but, seems that there is not another way.

Cheers.

静若繁花 2024-11-22 20:19:12

好吧,所以通过扩展方法混合将是我的首选路线。我不知道如何在 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

Alright so mix-ins through extension methods are going to be my preferred route. I couldn't figure out how to use dynamic proxies in vb.net (seemed to require libraries with lots of documentation that didn't cover specifically what I needed). Dynamic proxies also seems to be a bit dirtier of a solution than using extension methods. Composition would have been what I defaulted to if the previous two didn't work.

So one problem with extension methods, is that the code gets a little dirtier if you want to hold variables. Not much though. Another problem is that all the extension methods must be defined in modules, so the code might look a little goofy to a new eye. I will solve this by defining my interface and module with the corresponding extension method in the same file.

finally, here's some sample vb.net code if you don't want to see a full fledged example through the link.

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
你又不是我 2024-11-22 20:19:12

如果您只是必须具有多重继承,则肮脏的解决方案是使用 Java 中的动态代理。

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

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

Dirty solution, if you simply must have multiple inheritance, is using something like dynamic proxies in Java.

But I guess you're probably programming in C#, and this is language agnostic question, so here goes language agnostic answer: check out composite and factory design patterns, that should give you some ideas.

Also, it might not be needed to pass everything in constructor. Check out IoC pattern as well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文