Как интерфейсы достигают множественного наследования - PullRequest
7 голосов
/ 21 сентября 2011

Во многих книгах написано, что интерфейсы являются заменой множественного наследования, но я не вижу никакого сходства между ними обоими.

Наследование в основном важно для повторного использования кода и функциональности, и множественное наследование помогало повторно использовать код из более чем одного класса, но в интерфейсе я не нашел такой возможности, кроме того, что класс можетнаследовать от более чем одного интерфейса.

Интерфейс - это просто объявление функций / методов, и он сам по себе не содержит какой-либо части реализации, поэтому класс, наследующий этот интерфейс, должен написать свой собственный код реализации.

Так что я не чувствую возможности повторного использования кода в случае интерфейса.

Любой документ или ссылка, которая прояснит мои сомнения, ответьте, пожалуйста.

Ответы [ 6 ]

3 голосов
/ 21 сентября 2011

По поводу повторного использования кода вы правы. В этом отношении несколько интерфейсов не являются заменой множественного наследования.

Однако есть еще один аспект наследования: он устанавливает 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 голосов
/ 21 сентября 2011

Вы правы.

Люди подразумевают, что тип в C # может реализовывать несколько интерфейсов.Это не то же самое, что классическое наследование.

Это действительно позволяет вам использовать один класс во многих различных контекстах, и любой класс, унаследованный от такого класса, конечно, сможет использоватьреализация базового класса для повторного использования.

0 голосов
/ 04 апреля 2014

На самом деле, у меня нет хорошего ответа, кроме C #, у Java нет множественного наследования, и он ДОЛЖЕН иметься.Весь смысл в том, что интерфейсы должны быть в состоянии заменить необходимость множественного наследования, похоже на большую ложь, которая, когда повторяется достаточно много раз, становится правдой.

Аргумент в том, что множественное наследование вызывает все эти проблемы, но я продолжаю слышать эти аргументы от C #, разработчиков Java, которые никогда не использовали C ++.Я также НИКОГДА не помню, чтобы программисты на С ++ говорили: «Ну и дела, я люблю С ++, но если бы они избавились только от множественного наследования, это стало бы отличным языком».Люди использовали это, когда это было практично, и не, когда это не было.

Ваша проблема - это классический случай, когда подходит множественное наследование.Любое предложение по рефакторингу кода на самом деле говорит вам, как обойти ПРОБЛЕМУ, что у C #, Java нет множественного наследования.

Также все обсуждения о том, что «да, делегирование лучше, ля-ди-да»путая религию с дизайномТам нет правильного пути.Вещи либо более полезны, либо менее полезны, и это все.

В вашем случае множественное наследование было бы более полезным и более элегантным решением.

Поскольку рефакторинг вашего кода в менее полезную форму удовлетворяет всех религиозных людей, которые никогда не использовали множественное наследование и считают, что «множественное наследование - это плохо», я думаю, вам придется понизить код, потому что я неНе вижу C #, Java "улучшается" таким образом в ближайшее время.Слишком много людей повторяют религиозную мантру до такой степени глупости, что я не вижу, чтобы она когда-либо добавлялась к языку.

... но, как вы можете видеть из решений, представленных выше, большинство людей думают, что такое решение будет СЛИШКОМ СЛОЖНЫМ И ЗАМЕДЛЕННЫМ!

Я бы предпочел сам рискнуть на территорию "x расширяет a, b", даже если это очень пугающее решение, которое может превзойти возможности большинства программистов на C #, Java.

Что еще более удивительно в предложенных выше решениях, так это то, что все присутствующие, которые предложили вам преобразовать ваш код в «делегирование» из-за плохого множественного наследования, будут, если бы столкнулись с той же самой проблемой,Решите проблему, просто выполнив: «x расширяет a, b» и покончите с этим, и все их религиозные аргументы о «делегировании против наследования» исчезнут.Вся эта дискуссия глупа, и ее продвигают только невежественные программисты, которые только демонстрируют, насколько хорошо они могут читать из книги и как мало они могут думать сами.

Вы на 100% правы, что множественное наследованиепомогите, и нет, вы делаете что-то не так в своем коде, если думаете, что C #, Java должен иметь это.

0 голосов
/ 21 сентября 2011

Наследование объединяет два несколько ортогональных понятия:

  1. Производный класс может неявно использовать методы и поля родительского класса для обработки тех частей его поведения, которые соответствуют таковым из родительского класса.
  2. Объекты производного класса могут использоваться практически везде, где могут использоваться объекты родительского класса.

Разрешение классу неявно использовать методы и поля из более чем одного родительского класса может привести к некоторым сложным проблемам, например, если класс X предоставляет абстрактный метод foo (), классы Y и Z оба наследуют X и предоставляют разныереализации foo (), и класс Q пытается наследовать Y и Z. Если объект o класса Q передается методу, ожидающему X (допустимый, поскольку объекты производного класса могут использоваться там, где объекты родительского класса могутиспользовать), что должно произойти, если этот метод пытается вызвать o.foo ()?

Таких проблем, как правило, не существует с интерфейсами.Если интерфейсы IY и IZ наследуются от интерфейса IX, который содержит foo (), и интерфейс IQ наследует оба из них, класс, реализующий IQ, должен предоставить реализацию для IQ.foo (), которая также будет использоваться для IY.foo (),IZ.foo () и IX.foo ().Можно создавать неоднозначные ситуации, но они приводят к воплям во время компиляции.

0 голосов
/ 21 сентября 2011
 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 #, использующий C # 1.0).Но для разработчиков 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 # с интерфейсами и делегатами и Реализация подпрограмм обратного вызова в Java .

ПРИМЕЧАНИЕ: Пример кода в этом постевзято из книги C # 4.0 в двух словах (которая также является хорошим справочником по C #).

Известные разработчики предупреждали нас об опасностях наследования: Почему простирается зло Аллен Голуб ООП. Хорошие части: передача сообщений, утка, композиция, а не наследование , Ник Фитцджеральд Семь смертных грехов программирования - Грех №2: Чрезмерное использование наследования Эрик Ганнерсон

0 голосов
/ 21 сентября 2011

См. Этот пример.

 /*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");
        }
    }   

Используя концепцию интерфейса, класс может наследовать один или несколько интерфейсов.И может реализовать собственную функциональность для унаследованных интерфейсов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...