【问题标题】:How interfaces achieve multiple inheritance接口如何实现多重继承
【发布时间】:2011-11-21 20:10:17
【问题描述】:

在许多书中都写到接口是多重继承的替代品,但我没有发现它们之间有任何相似之处。

继承对于代码和功能的可重用性最重要,多重继承有助于重用来自多个类的代码,但在接口中我没有找到任何这样的特性,除了一个类可以从更多类继承不止一个接口。

接口只是函数/方法的声明,它本身不包含任何实现部分,因此继承该接口的类必须编写自己的实现代码。

所以在接口的情况下,我不觉得代码有任何可重用性。

是否有任何文件或链接可以解决我的疑问,请与您分享。

【问题讨论】:

  • “继承对于代码的可重用性最重要” - 不,这是一个次要方面。
  • 继承的其他方面是什么
  • 您想使用类和继承来为您的应用程序域构建一个逻辑一致的模型。在需要的地方重复使用。
  • 多重实现继承是一个错误工厂,因此.NET明确不支持。 en.wikipedia.org/wiki/Diamond_problem
  • 您能否详细探讨一下如何使用继承来构建应用领域的逻辑一致模型。

标签: c# oop interface


【解决方案1】:

关于代码的可重用性,您是对的。在这方面,多接口并不能替代多继承。

但是,继承还有另一个方面:它在基类和子类之间建立了is-a 关系。因此,一个继承多个超类的类可以充当其中的任何一个。在这方面,接口可以作为有效的替代品,例如接受接口的方法也将接受实现该接口的任何类,就像方法将接受从预期类派生的任何类一样。但是正如您所说,每个类都必须自己实现接口方法。

例子:

public interface Foo {
    int doFoo();
}

public interface Bar {
    long doBar();
}

public class Baz {
    String doBaz() {
        return "This is baz";
    }
}

public class FooBar extends Baz implements Foo, Bar {
    public long doBar() {
        return 123;
    }
    public int doFoo() {
        return 456;
    }
}

// Accepts interface Bar implementing objects
public void doSomething(Bar b) {
    System.out.println(b.doBar() * 10);
}

// Accepts interface Foo implementing objects
public void doSomethingOther(Foo f) {
    System.out.println(f.doFoo() / 10);
}

// Accepts objects of class Baz and subclasses
public void doMore(Baz b) {
    System.out.println(b.doBaz());
}

void bla() {
    FooBar fb = new FooBar();

    // FooBar can act as Foo, Bar, and Baz
    doSomething(fb);
    doSomethingOther(fb);
    doMore(fb);
}

【讨论】:

  • 你能否用一些示例代码详细探讨“接受接口的方法....”这一点。
【解决方案2】:

你是对的。

人们的意思是C#中的一个类型可以实现多个接口。这与经典继承不同。

确实允许您在许多不同的上下文中使用一个类,并且从此类继承的任何类当然都可以使用基类实现进行重用。

【讨论】:

    【解决方案3】:

    请参阅此示例。

     /*interface 1 with two methods*/
        interface IA1
        {
            void Foo1();
            void Foo2();
        }   
       /*Interface two with two methods */
        interface IA2
        {
            void Foo1();
            void Foo3();
        }   
    
    
    
     /*class implemeting two interfaces now. Here class cannot inherit two classes but can inherit two interfcaes.*/
      /* Case1 */
        class CA : IA1, IA2  
        { 
            void IA1.Foo1()//Explicitly Implemented
            {
                Console.WriteLine("In IA1.Foo1");
            }
            void IA2.Foo1() //Explicitly Implemented
            {
                Console.WriteLine("In IA2.Foo1");
            }
            public void Foo2()  //Implicitly Implemented
            {
                Console.WriteLine("In IA1.Foo2");
            }
            public void Foo3()  //Implicitly Implemented
            {
                Console.WriteLine("In IA2.Foo3");
            }
        }
    
    
     /* Case2*/
        class CA : IA1, IA2   {
            public void Foo1() //Implicitly Implemented
            {
                Console.WriteLine("In Foo1");
            }
            public void Foo2()  //Implicitly Implemented
            {
                Console.WriteLine("In Foo2");
            }
            public void Foo3()  //Implicitly Implemented
            {
                Console.WriteLine("In Foo3");
            }
        }   
    

    使用接口的概念,一个类可以继承一个或多个接口。并且可以为继承的接口实现自己的功能。

    【讨论】:

      【解决方案4】:
       Inheritance is mostly important for re-usability of code and functionality 
       and multiple inheritance was helping to re-use code from more than one class, 
       but in Interface I didn't find any such feature except that a class can inherit 
       from more than one interface.
      

      确实,接口促进了代码的可重用性(尤其是与多态性结合时,继承可以创造奇迹!)。我可以举出另一种接口可能有益的情况:回调。

      由于我们在 C# 中有委托,我怀疑是否有任何 C# 开发人员会使用接口作为回调的媒介(可能是使用 C# 1.0 的 C# 开发人员)。但是对于 Java 开发人员,他们将如何在没有委托的情况下实现回调?答案:接口。

      使用委托的回调

      public delegate int Transformer (int x);
      
      class Util
      {
          public static void Transform (int[] values, Transformer t)
          {
              for (int i = 0; i < values.Length; i++)
                  values[i] = t (values[i]);
          }
      }
      
      class Test
      {
          static void Main()
          {
              int[] values = { 1, 2, 3 };
              Util.Transform (values, Square);
              foreach (int i in values)
                  Console.Write (i + " "); 
          }
      
          static int Square (int x) { return x * x; }
      }
      

      使用接口的回调

      public interface ITransformer
      {
          int Transform (int x);
      }
      
      public class Util
      {
          public static void TransformAll (int[] values, ITransformer t)
          {
              for (int i = 0; i < values.Length; i++)
                  values[i] = t.Transform (values[i]);
          }
      }
      
      class Squarer : ITransformer
      {
          public int Transform (int x) { return x * x; }
      }
      
      static void Main()
      {
          int[] values = { 1, 2, 3 };
          Util.TransformAll (values, new Squarer());
          foreach (int i in values)
               Console.WriteLine (i);
      }
      

      如需进一步了解回调,请参阅C# Callbacks with Interfaces and DelegatesImplement callback routines in Java

      注意:本文中的示例代码摘自《C# 4.0 in a Nutshell》一书(这也是一本很好的 C# 参考资料)。

      知名开发人员警告我们继承的危险:
      Why extends is evil Allen Holub
      OOP The Good Parts: Message Passing, Duck Typing, Object Composition, and not Inheritance Nick Fitzgerald
      Seven deadly sins of programming - Sin #2: Overuse of Inheritance Eric Gunnerson

      【讨论】:

      • 接口有时可能是一种很好的回调机制。例如,包装互斥体(可能是 Monitor 锁、Semaphore、SemaphoreSlim 或 BobsSuperDeluxeMutex)的“获取”例程可以返回 MethodInvoker 或 IDisposable 以提供其释放,但通常的做法是 IDisposable .
      【解决方案5】:

      继承结合了两个有些正交的概念:

      1. 派生类可以隐式使用父类的方法和字段来处理其行为中与父类匹配的那些部分。
      2. 派生类的对象几乎可以在任何可以使用父类对象的地方使用。

      允许一个类隐式使用来自多个父类的方法和字段可能会导致一些棘手的问题,例如如果 X 类提供了一个抽象方法 foo(),那么 Y 和 Z 类都继承了 X 并提供了 foo() 的不同实现,而 Q 类试图继承 Y 和 Z。如果将 Q 类的对象 o 传递给期望的方法一个 X(合法的,因为派生类的对象可以在可以使用父类的对象的地方使用),如果该方法尝试调用 o.foo() 会发生什么?

      接口通常不存在此类问题。如果接口 IY 和 IZ 继承自包含 foo() 的接口 IX,并且接口 IQ 继承这两者,则实现 IQ 的类必须提供 IQ.foo() 的实现,该实现也将用于 IY.foo(), IZ.foo() 和 IX.foo()。可能会产生模棱两可的情况,但这些情况会导致编译时出现问题。

      【讨论】:

        【解决方案6】:

        实际上,除了 C#,我没有好的答案,Java 没有多重继承,它应该有。接口应该能够取代对多重继承的需求这一点就像一个大谎言,当重复足够多的时间就会变成真的。

        争论是多重继承导致了所有这些问题,但我不断听到 C# 的争论,Java 开发人员从未使用过 C++。我也不记得 C++ 程序员说过“天啊,我喜欢 C++,但如果他们只摆脱多重继承,它就会成为一门很棒的语言”。人们在实用时使用它,而在不实用时则不使用。

        您的问题是适合多重继承的经典案例。任何重构代码的建议实际上都是在告诉您如何解决 C#、Java 没有多重继承的问题。

        还有所有关于“哦,授权更好,la-di-dah”的讨论都将宗教与设计混为一谈。没有正确的方法。事物要么更有用,要么更没用,仅此而已。

        在您的情况下,多重继承将是更有用且更优雅的解决方案。

        至于将代码重构为不太有用的形式以满足所有从未使用过多重继承并相信“多重继承不好”的宗教人士,我想你将不得不降级你的代码,因为我没有看到C#、Java 很快就会以这种方式“改进”。有太多人重复宗教咒语到愚蠢的地步,我看不到它被添加到语言中。

        ...但是从上面提供的解决方案中可以看出,大多数人认为这样的解决方案太复杂和令人困惑!

        我更愿意自己冒险进入“x 扩展 a,b”领域,即使这是一个非常可怕的解决方案,可能会压倒大多数 C#、Java 程序员的能力。

        上面建议的解决方案更令人惊奇的是,这里的每个人都建议您将代码重构为“委托”,因为多重继承不好,如果他们遇到同样的问题,他们会解决问题通过简单地做:“x extends a,b”并完成它,他们所有关于“委托与继承”的宗教争论都会消失。整个辩论都是愚蠢的,而且只有无知的程序员才会推动它,他们只会证明他们能从书中背诵得有多好,而他们自己思考的能力有多少。

        您 100% 正确地认为多重继承会有所帮助,不,如果您认为 C#、Java 应该有它,那么您的代码做错了什么。

        【讨论】:

        • 没有@MattDavey!这是一个现实。
        • 这是您的意见,仅此而已。
        猜你喜欢
        • 2011-10-07
        • 2013-10-22
        • 1970-01-01
        • 2019-03-06
        • 1970-01-01
        • 1970-01-01
        • 2010-09-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多