Как интерфейсы могут заменить необходимость множественного наследования при наличии существующих классов
прежде всего... Извините за этот пост. Я знаю, что есть много постов на StackOverflow, которые обсуждают множественное наследование. Но я уже знаю, что Java не поддерживает множественное наследование, и я знаю, что использование интерфейсов должно быть альтернативой. Но я не понимаю этого и вижу свою дилемму:
Я должен внести изменения в очень большой и сложный инструмент, написанный на Java. В этом инструменте есть структура данных, построенная с большим количеством различных объектов класса иерархия связанных элементов. В любом случае...
- у меня есть один класс
Tagged, который имеет несколько методов и возвращает тег объекта в зависимости от класса объекта. Ему нужны члены и статические переменные. - и второй класс
XMLElementпозволяет связывать объекты и в конечном итоге генерировать XML-файл. Мне также нужны член и статические переменные здесь. - наконец, у меня есть эти многие классы данных, которые почти все должны расширяться
XMLElementи некоторые из нихTagged.
Ок ок, это не будет работать, так как можно расширить только один класс. Я очень часто читаю, что все с Java в порядке, и нет необходимости иметь множественное наследование. Я верю, но я не вижу, как интерфейс должен заменить наследования.
- нет смысла помещать реальную реализацию во все классы данных, так как она одинакова каждый раз, но это было бы необходимо с интерфейсами (я думаю).
- Я не Смотри Как я могу изменить один из моих классов наследование интерфейса. У меня есть переменные здесь, и они должны быть именно там.
Я действительно не понимаю поэтому, пожалуйста, может кто-нибудь объяснить мне, как справиться с этим?
9 ответов:
вероятно, вы должны отдавать предпочтение композиции (и делегированию) над наследованием:
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(); } }
на самом деле, у меня нет хорошего ответа, кроме Java должен иметь множественное наследование. Все дело в том, что интерфейсы должны быть в состоянии заменить необходимость множественного наследования, как большая ложь, которая при повторении достаточно раз становится правдой.
аргумент заключается в том, что множественное наследование вызывает все эти проблемы (la-di-dah), но я продолжаю слышать эти аргументы от разработчиков Java, которые никогда не использовали C++. Я также никогда не помню, чтобы программисты C++ говорили: "Боже, я люблю C++, но если бы они только избавились от множественного наследования, это стало бы отличным языком". Люди использовали его, когда это было практично, и не делали, когда это было не так.
ваша проблема является классическим случаем, когда множественное наследование было бы уместно. Любое предложение по рефакторингу кода действительно говорит вам, как обойти проблему, что Java не имеет множественного наследования.
также вся дискуссия о том, что "о, делегирование лучше, Ла-Ди-да" сбивает с толку религия с дизайном. Нет никакого правильного пути. Вещи либо более полезны, либо менее полезны, и это все.
в вашем случае множественное наследование было бы более полезным и более элегантным решением.
Что касается рефакторинга вашего кода в менее полезную форму, чтобы удовлетворить всех религиозных людей, которые никогда не использовали множественное наследование и считают, что" множественное наследование плохо", я думаю, вам придется понизить свой код, потому что я не вижу "улучшения" Java в этом в ближайшее время. Слишком много людей повторяют религиозную мантру до такой степени глупости, что я не вижу, чтобы она когда-либо добавлялась к языку.
на самом деле, мое решение для вас будет "x extends Tagged, XMLElement", и это будет все.
...но, как вы можете видеть из приведенных выше решений, большинство людей думают, что такое решение было бы слишком сложным и запутанным!
Я бы предпочел рискнуть в "x расширяет a, b" территорию себе, даже если это очень пугающее решение, которое может подавить способности большинства Java-программистов.
Что еще более удивительно в решениях, предложенных выше, так это то, что все здесь, кто предложил вам рефакторинг вашего кода в "делегирование", потому что множественное наследование плохо,если бы они столкнулись с одной и той же проблемой, решили бы проблему, просто сделав: "x расширяет a, b" и покончить с этим, и все их религиозные аргументы о том, что "делегирование против наследования" исчезнет. Вся эта дискуссия глупа, и ее продвигают только невежественные программисты, которые только демонстрируют, насколько хорошо они могут читать из книги и как мало они могут думать сами.
вы на 100% правы, что множественное наследование поможет, и нет, вы делаете что-то неправильно в своем коде, если вы думаете, что Java должен иметь его.
во-первых, нет смысла помещать реальную реализацию во все классы данных, поскольку она одинакова каждый раз, но это было бы необходимо с интерфейсами (я думаю).
как насчет использования агрегации для тегов?
переименовать
TaggedклассTags.создать
Taggedинтерфейс:интерфейс с тегами { Метить getTags(); }
пусть каждый класс, который должен быть "помечен", реализовать
Taggedи пусть это было!--4--> поле, которое возвращается изgetTags.во-вторых, я не вижу, как я мог бы изменить один из моих классов наследования на интерфейс. У меня есть переменные здесь, и они должны быть именно там.
правильно, интерфейсы не могут иметь переменные экземпляра. Данные однако структуры, хранящие теги, не обязательно должны быть частью классов, которые помечены. Разложите теги в отдельную структуру данных.
похоже на то, что 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(); } }
Я бы решил это так: извлеките интерфейсы для
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)
одним из возможных способов;
1-Вы можете создать базовый класс (ы) для общей функциональности, сделать его абстрактным, если вам не нужно создавать его экземпляр.
2-создание интерфейсов и реализация этих интерфейсов в этих базовых классах. Если требуется конкретная реализация, сделайте метод абстрактным. каждый конкретный класс может иметь свой собственный impl.
3 - расширить абстрактный базовый класс для конкретного класса (ов) и реализовать конкретные интерфейсы на этом уровне как ну
просто интересно, нельзя ли просто использовать внутренние (членские) классы (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 у вас есть доступ к не закрытым членам классов-членов. Конечно, никто не будет использовать "супер", но вызывать методы класса-члена. В качестве альтернативы можно было бы расширить один из классов и сделать другой член класса. Есть некоторые ограничения, но я думаю, что их все можно обойти.
использование композиции было бы способом пойти, как предложил другой разработчик. Основным аргументом против множественного наследования является неоднозначность, создаваемая при расширении из двух классов с одним и тем же объявлением метода (одно и то же имя метода и параметры). Лично, однако, я думаю, что это чушь. Ошибка компиляции может быть легко вызвана в этой ситуации, которая не будет сильно отличаться от определения нескольких методов с одинаковым именем в одном классе. Что-то вроде следующий фрагмент кода может легко решить эту дилемму:
public MyExtendedClass extends ClassA, ClassB { public duplicateMethodName() { return ClassA.duplicateMethodName(); } }еще один аргумент против множественного наследования заключается в том, что Java пыталась сохранить все просто, чтобы разработчики-любители не создавали сеть взаимозависимых классов, которые могли бы создать беспорядочную, запутанную программную систему. Но, как вы видите в вашем случае, это также усложняет и запутывает вещи, когда они недоступны. Кроме того, этот аргумент может быть использован для 100 других вещей в кодировании, поэтому команды разработчиков есть обзоры кода, программное обеспечение для проверки стиля и ночные сборки.
однако в вашей конкретной ситуации вам придется согласиться с композицией (см. ответ Shojaei Baghini). Он добавляет немного кода котельной плиты, но он эмулирует то же поведение, что и множественное наследование.
Я запускаю аналогичную проблему на Android. Мне нужно было расширить кнопку и 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 } } }Итак, вот в основном мой класс модификатора класс, который инкапсулирует все реализации.
надеюсь, это кому-то поможет.
Comments