多重继承和C#

4
假设我有两个完全不同功能的基础抽象类:Laptop和Smartphone。在当前项目中,我已经有了许多笔记本电脑和智能手机的实现,并且它们始终是完全不同的。
但突然间,我收到了一个请求,要添加一个同时具有智能手机和笔记本电脑功能的PC平板类的实现。现在改变基础类太晚了,而且我非常确定这个PC平板只会出现一次。
问题是,我应该能够将我的PC平板放入智能手机的容器中,但它也应该是笔记本电脑,因为它继承了相应的功能(实际上,在项目的某些部分中,PC平板仅用作笔记本电脑,并且不需要智能手机功能。此外,对于该特定部分的项目来说,将PC平板视为智能手机是不好的)。因此,我创建了PcTabletAsLaptop:Laptop类,它实际上是一个笔记本电脑,而不是智能手机。
我的解决方案是添加一个包装器:
class PcTablet : SmartPhone
{
    private PcTabletAsLaptop _pcTablet;

    // Here goes all the methods of PcTabletAsLaptop as proxies:

    public void Call(int number)
    {
      _pcTablet.Call(number);
     }

     // .....
}

有200多种方法,我希望它们可以从PcTabletAsLaptop自动生成。

这个解决方案看起来很复杂。我的问题是它是否好用,或者有更简单的方法吗?


你能不能添加一个名为“MobileDevice”的基类,让它们都继承自它呢? - Dave Bish
200多个方法?也许现在是考虑重新设计的时候了。 - Chris Sinclair
@DaveBish 我不想改变原有的层次结构,因为该项目已经在生产中。实际上,这是客户提出的要求(我们正在编写框架,因此代码设计也被视为要求的一部分)。 - Archeg
@ChrisSinclair 许多移动设备都是围绕着C++ dll的包装器,具有最小的逻辑,包括这个。在大多数情况下,这些包装器是由某些第三方生成的,我们对此没有任何控制,但是我们有包装器源代码,可以更改它们。仍然不应该进行完全重新设计,因为由于C++ dll中的任何更改,应该能够以最小的更改从第三方获取包装器。 - Archeg
3个回答

3
你可以从智能手机和笔记本电脑中提取一个接口,然后创建第三个接口PcTablet,该接口将继承前两个接口。这样,你就能够将PcTablet用作智能手机或笔记本电脑。
编辑:
为了能够重用每个智能手机和笔记本电脑中的逻辑,你可以使用适配器模式,因此PcTablet应该看起来像这样:
public class PcTablet :ISmartPhone, ILaptop
{
     private SmartPhone _smartphone;
     private Laptop _laptop;
     public void ISmartPhone.Call()
     {
         _smartPhone.Call();
         // OR IMPLEMENT THE RIGHT BEHAVIOR 
         //INSTEAD OF CALLING _smartPhone.Call()
     }
}

当然,您需要在构造函数中创建智能手机和笔记本电脑,但这应该可以解决问题!这样,您就可以在笔记本电脑和智能手机中重复使用代码,并在它们提供错误行为的情况下覆盖它们。


不错的想法,但是智能手机和笔记本电脑基类的逻辑怎么办?我希望能够以最小的努力重新利用它们。 - Archeg
没错,这已经足够好了,而且这也是我的第一意图。唯一让我担心的是,我必须为_smartphone和_laptop的每个方法实现这些包装器(它们有很多)。 - Archeg
@Archeg 是的,我明白,但是为了能够同时将您的PCTablet用作笔记本电脑和智能手机,并且能够重用两个类的代码…除此之外,我没有看到更干净的方法。而且它足够灵活,如果-比如说Call函数-不按照您希望的方式工作,您可以自由更改实现而不影响基类。相反地,您对基类所做的所有更改都会影响PCTablet类,这既有利又有弊,但是再次强调,您可以覆盖该行为! - Jean-Christophe Fortin

1

如果您需要多重继承 - 在大多数情况下,您正在做错事情或尝试以错误的方式解决问题。以下是类层次结构如何?

internal class PcTabletAslaptop : LaptopBase
{
    // here is you can expose / override laptop specific stuff
}

internal class PcTabletAsSmartphone : SmartphoneBase
{
    // here is you can expose / override smartphone specific stuff
}

public interface IPcTablet
{
   // just expose PcTablet specific API
}

public sealed class PcTablet : IPcTablet
{
   private PcTabletAsSmartphone asSmartphone;
   private PcTabletAsLaptop asLaptop;
}

你的解决方案看起来和我的很像,只是我从SmartPhone派生出了PcTablet。我猜你的解决方案甚至可能更好。但是我仍然需要为这200多个方法制作代理,以便PcTablet调用其中一个字段。所以,我的问题是这样做的方式是否正确? - Archeg
那么,对于PcTablet类来说,笔记本电脑和智能手机类的公共API是不够的,您需要访问受保护/私有接口吗?如果是这样,请举一个需要访问笔记本电脑/智能手机的私有/受保护接口的例子。 - sll
我的意思是PcTabletAsSmartphone有Call()方法,那么PcTablet也应该有Call()方法,调用asSmartphone.Call()。我有200多个这样的方法。由于我隐藏了PcTabletAsSmartphone,所以我需要将此方法编写为代理到隐藏的内部实例。当然,我可以公开asSmartphone实例,但这将破坏所有设计。 - Archeg

0

我认为你可以从组合的角度来思考,而不是聚合。

聚合与组合

如果是 SmartPhone 包含 Tablet 呢?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接