Подтвердить что ты не робот

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

Прежде всего... Извините за этот пост. Я знаю, что в stackoverflow есть много сообщений, которые обсуждают множественное наследование. Но я уже знаю, что Java не поддерживает множественное наследование, и я знаю, что использование интерфейсов должно быть альтернативой. Но я не понимаю и вижу свою дилемму:

Мне нужно внести изменения в очень большой и сложный инструмент, написанный на Java. В этом инструменте есть структура данных, построенная с множеством различных объектов класса с иерархией связанных элементов. В любом случае...

  • У меня есть один класс Tagged, который имеет несколько методов и возвращает тег объекта в зависимости от класса объекта. Ему нужны члены и статические переменные.
  • И второй класс под названием XMLElement позволяет связывать объекты и в конце генерировать XML файл. Мне также нужны члены и статические переменные.
  • Наконец, у меня есть много многих классов данных, которые почти все должны расширять XMLElement и некоторые из них Tagged.

Хорошо, хорошо, это не сработает, так как это возможно только для одного класса. Я читал очень часто, что все с Java в порядке, и нет необходимости иметь множественное наследование. Я считаю, но я не вижу, как интерфейс должен заменить наследование.

  • Не имеет смысла ставить реальную реализацию во всех классах данных, так как каждый раз это одно и то же, но это необходимо с интерфейсами (я думаю).
  • Я не вижу, как я могу изменить один из моих классов наследования на интерфейс. Здесь у меня есть переменные, и они должны быть именно там.

Я действительно не понимаю, пожалуйста, может кто-нибудь объяснить мне, как с этим справиться?

4b9b3361

Ответ 1

Вероятно, вы должны одобрить состав (и делегирование) над наследованием:

public interface TaggedInterface {
    void foo();
}

public interface XMLElementInterface {
    void bar();
}

public class Tagged implements TaggedInterface {
    // ...
}

public class XMLElement implements XMLElementInterface {
    // ...
}

public class TaggedXmlElement implements TaggedInterface, XMLElementInterface {
    private TaggedInterface tagged;
    private XMLElementInterface xmlElement;

    public TaggedXmlElement(TaggedInterface tagged, XMLElementInterface xmlElement) {
        this.tagged = tagged;
        this.xmlElement = xmlElement;
    }

    public void foo() {
        this.tagged.foo();
    }

    public void bar() {
        this.xmlElement.bar();
    }

    public static void main(String[] args) {
        TaggedXmlElement t = new TaggedXmlElement(new Tagged(), new XMLElement());
        t.foo();
        t.bar();
    }
}

Ответ 2

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

Аргумент заключается в том, что множественное наследование вызывает все эти проблемы (la-di-dah), но я продолжаю слышать эти аргументы от разработчиков Java, которые никогда не использовали С++. Я также не помню, как программисты на C++ говорили: "Джи, я люблю С++, но если они только избавятся от Multiple Inheritance, это станет отличным языком". Люди использовали его, когда это было практично, а когда нет.

Ваша проблема - классический пример того, как многократное наследование было бы уместным. Любое предложение по рефакторингу кода действительно говорит вам, как работать с проблемой, что Java не имеет множественного наследования.

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

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

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

На самом деле, мое решение для вас будет "x расширяет Tagged, XMLElement", и все это будет.

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

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

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

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

Ответ 3

Сначала бессмысленно ставить реальную реализацию во всех классах данных, так как каждый раз это одно и то же, но это необходимо с интерфейсами (я думаю).

Как насчет использования агрегации для тегов?

  • Переименуйте класс Tagged в Tags.

  • Создайте интерфейс Tagged:

    интерфейс Tagged {   Теги getTags(); }

  • Пусть каждый класс, который должен быть "помечен", реализует Tagged и пусть он имеет поле Tags, которое возвращается из getTags.

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

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

Ответ 4

Подобно тому, что предлагалось Andreas_D, но с использованием внутренних классов. Таким образом, вы действительно расширяете каждый класс и можете при необходимости переопределить его в своем собственном коде.

interface IBird {
    public void layEgg();
}

interface IMammal {
    public void giveMilk();
}

class Bird implements IBird {
    public void layEgg() {
        System.out.println("Laying eggs...");
    }
}

class Mammal implements IMammal {
    public void giveMilk() {
        System.out.println("Giving milk...");
    }
}

class Platypus implements IMammal, IBird {

    private class LayingEggAnimal extends Bird {}
    private class GivingMilkAnimal extends Mammal {}

    private LayingEggAnimal layingEggAnimal = new LayingEggAnimal();

    private GivingMilkAnimal givingMilkAnimal = new GivingMilkAnimal();

    @Override
    public void layEgg() {
        layingEggAnimal.layEgg();
    }

    @Override
    public void giveMilk() {
        givingMilkAnimal.giveMilk();
    }
}

Ответ 5

Я бы так решил: извлечь интерфейсы для классов Tagged и XMLElement (возможно, вам не нужны все методы в открытом интерфейсе). Затем реализуйте оба интерфейса, а класс реализации имеет Tagged (ваш фактический конкретный класс Tagged) и XMLElement (ваш фактический конкретный класс XMLElement):

 public class MyClass implements Tagged, XMLElement {

    private Tagged tagged;
    private XMLElement xmlElement;

    public MyClass(/*...*/) {
      tagged = new TaggedImpl();
      xmlElement = new XMLElementImpl();
    }

    @Override
    public void someTaggedMethod() {
      tagged.someTaggedMethod();
    }
  }

  public class TaggedImpl implements Tagged {
    @Override
    public void someTaggedMethod() {
      // so what has to be done
    }
  }

  public interface Tagged {
     public void someTaggedMethod();
  }

(и то же самое для XMLElement)

Ответ 6

один из возможных способов;

1- Вы можете создать базовый класс для общей функциональности, сделать его абстрактным, если вам не нужно его создавать.

2- Создавайте интерфейсы и реализуйте эти интерфейсы в этих базовых классах. Если требуется конкретная реализация, сделайте метод абстрактным. каждый конкретный класс может иметь свой собственный.

3- расширять базовый класс абзацев в конкретных классах (классах) и реализовывать на этом уровне определенные интерфейсы

Ответ 7

Просто интересно, нельзя ли просто использовать внутренние (членские) классы (LRM 5.3.7)? Например. как это (на основании первого ответа выше):

// original classes:
public class Tagged {
    // ...
}

public class XMLElement {
    // ...
}

public class TaggedXmlElement {
    public/protected/private (static?) class InnerTagged extends Tagged {
      // ...
    }

    public/protected/private (static?) class InnerXmlElement extends XMLElement  {
        // ...
    }

}

Таким образом, у вас есть класс TaggedXmlElement, который фактически содержит все элементы из двух исходных классов, а в TaggedXmlElement у вас есть доступ к не-частным членам классов-членов. Конечно, не следует использовать методы "супер", а также методы класса вызовов. В качестве альтернативы можно расширить один из классов и сделать другой класс-член. Есть некоторые ограничения, но я думаю, что все они могут работать.

Ответ 8

Использование композиции было бы способом пойти другим разработчиком. Основной аргумент против множественного наследования - это двусмысленность, созданная при расширении от двух классов с тем же объявлением метода (такое же имя метода и параметры). Лично, однако, я думаю, что нагрузка дерьма. В этой ситуации легко можно было бы скомпилировать ошибку компиляции, которая не сильно отличалась бы от определения нескольких методов с одним и тем же именем в одном классе. Что-то вроде следующего фрагмента кода может легко решить эту дилемму:

public MyExtendedClass extends ClassA, ClassB {
    public duplicateMethodName() {
        return ClassA.duplicateMethodName();
    }
}

Другим аргументом против множественного наследования является то, что Java пытается упростить задачу, чтобы любительские разработчики не создавали сеть взаимозависимых классов, которая могла бы создать беспорядочную, запутанную систему программного обеспечения. Но, как вы видите в своем случае, это также усложняет и путает вещи, когда они недоступны. Кроме того, этот аргумент можно использовать для 100 других вещей в кодировании, поэтому команды разработчиков имеют обзоры кода, программное обеспечение для проверки стилей и ночные сборки.

В вашей конкретной ситуации, однако, вам придется довольствоваться композицией (см. ответ Shojaei Baghini). Он добавляет немного кода плиты котла, но он эмулирует то же поведение, что и множественное наследование.

Ответ 9

Я запускаю аналогичную проблему на Android. Мне нужно было расширить Button и TextView (оба наследующие от View) с дополнительными функциями. Из-за отсутствия доступа к их суперклассу мне нужно было найти другое решение. Я написал новый класс, который инкапсулирует все реализации:

class YourButton extends Button implements YourFunctionSet {
    private Modifier modifier;

    public YourButton(Context context) {
        super(context);
        modifier = new Modifier(this);
    }

    public YourButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        modifier = new Modifier(this);
    }

    public YourButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        modifier = new Modifier(this);
    }

    @Override
    public void generateRandomBackgroundColor() {
        modifier.generateRandomBackgroundColor();
    }
}

class Modifier implements YourFunctionSet {

    private View view;

    public Modifier(View view) {
        this.view = view;
    }

    @Override
    public void generateRandomBackgroundColor() {
        /**
         * Your shared code
         *
         * ......
         *
         * view.setBackgroundColor(randomColor);
         */
    }
}


interface YourFunctionSet {
    void generateRandomBackgroundColor();
}

Проблема в том, что вашим классам нужен тот же суперкласс. Вы также можете попробовать использовать разные классы, но проверьте, из какого типа он, например

public class Modifier{
private View view;
private AnotherClass anotherClass;

public Modifier(Object object) {
    if (object instanceof View) {
        this.view = (View) object;
    } else if (object instanceof AnotherClass) {
        this.anotherClass = (AnotherClass) object;
    }
}

public void generateRandomBackgroundColor(){
    if(view!=null){
        //...do
    }else if(anotherClass!=null){
        //...do
    }
}
}

Итак, вот в основном мой класс модификатора класс, который инкапсулирует все реализации.

Надеюсь, это поможет кому-то.