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

<p>прежде всего ... извините за этот пост. я знаю, что в stackoverflow есть много сообщений о множественном наследовании. но я уже знаю, что java не поддерживает множественное наследование, и я знаю, что использование интерфейсов должно быть альтернативой. но я не понимаю и вижу мою дилемму:</p><p>я должен внести изменения в очень очень большой и сложный инструмент, написанный на java. в этом инструменте есть структура данных, построенная из множества различных объектов класса со связанной иерархией элементов. тем не мение...</p><ul> <li>у меня есть один класс <code>tagged</code>, который имеет несколько методов и возвращает тег объекта в зависимости от класса объекта. ему нужны члены и статические переменные.</li> <li>а второй класс с именем <code>xmlelement</code> позволяет связывать объекты и, в конце концов, генерировать xml-файл. мне также нужны член и статические переменные здесь.</li> <li>наконец, у меня есть много классов данных, которые почти все должны расширять <code>xmlelement</code> и некоторые из них <code>tagged</code>.</li> </ul><p>хорошо, хорошо, это не сработает, так как возможно продлить только один класс. я очень часто читал, что с java все в порядке и нет необходимости иметь множественное наследование. я верю, но я не понимаю, как интерфейс должен заменить наследование.</p><ol> <li>нет смысла помещать реальную реализацию во все классы данных, поскольку она всегда одинакова, но это было бы необходимо для интерфейсов (я думаю).</li> <li>я не понимаю, как я мог бы изменить один из моих классов наследования на интерфейс. у меня есть переменные здесь, и они должны быть именно там.</li> </ol><p>я действительно не понимаю, поэтому, может, кто-нибудь объяснит мне, как справиться с этим?</p>

<p>вероятно, вам следует отдать предпочтение композиции (и делегированию), а не наследованию:</p><pre><code>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(); } } </code></pre>

<p>на самом деле, у меня нет хорошего ответа, кроме java должно иметь множественное наследование. весь смысл в том, что интерфейсы должны быть в состоянии заменить необходимость множественного наследования, похоже на большую ложь, которая, когда повторяется достаточно много раз, становится правдой.</p><p>аргумент заключается в том, что множественное наследование вызывает все эти проблемы (la-di-dah), но я продолжаю слышать эти аргументы от java разработчиков, которые никогда не использовали c++. я также никогда не помню, чтобы программисты на с ++ говорили: "ну и дела, я люблю с ++, но если бы они избавились только от множественного наследования, это стало бы отличным языком". люди использовали это, когда это было практично, и не, когда это не было.</p><p>ваша проблема - классический случай множественного наследования. любое предложение реорганизовать код действительно говорит вам, как обойти проблему, что java не имеет множественного наследования.</p><p>кроме того, все дискуссии о том, что "о, делегирование лучше, ла-ди-дах", путают религию с дизайном. там нет правильного пути. вещи или более полезны или менее полезны, и это все.</p><p>в вашем случае множественное наследование будет более полезным и более элегантным решением.</p><p>что касается рефакторинга вашего кода в менее полезную форму, чтобы удовлетворить всех религиозных людей, которые никогда не использовали множественное наследование и считают, что "множественное наследование - это плохо", я думаю, вам придется понизить код, потому что я не вижу java "улучшается" таким образом в ближайшее время. слишком много людей повторяют религиозную мантру до такой степени глупости, что я не вижу, чтобы она когда-либо добавлялась к языку.</p><p>на самом деле, мое решение для вас было бы "x extends tagged, xmlelement", и это было бы все.</p><p>... но, как вы можете видеть из решений, представленных выше, большинство людей думают, что такое решение будет слишком сложным и замедленным!</p><p>я бы предпочел рискнуть на территорию "x extends a, b" самостоятельно, даже если это очень пугающее решение, которое может превзойти возможности большинства java программистов.</p><p>что еще более удивительно в решениях, предложенных выше, так это то, что все, кто предлагал вам преобразовать ваш код в "делегирование" из-за плохого множественного наследования, могли бы, если бы столкнулись с той же самой проблемой, решить эту проблему, просто выполнив : "x расширяет a, b" и покончим с этим, и все их религиозные аргументы о "делегировании против наследования" исчезнут. вся эта дискуссия глупа, и ее продвигают только невежественные программисты, которые только демонстрируют, насколько хорошо они могут читать из книги и как мало они могут думать сами за себя.</p><p>вы на 100% правы, что множественное наследование поможет, и нет, вы делаете что-то не так в своем коде, если считаете, что java должно иметь это.</p>

<p>подобно тому, что <a href="https://stackoverflow.com/a/5003341/1426227">предложил andreas\_d</a> но с использованием внутренних классов. таким образом, вы действительно расширяете каждый класс и при желании можете переопределить его в своем собственном коде.</p><pre><code>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(); } } </code></pre>

<blockquote> <p><em> <strong>во-первых, нет смысла помещать реальную реализацию во все классы данных, поскольку она каждый раз одинакова, но это было бы необходимо для интерфейсов (я думаю).</strong> </em></p> </blockquote><p>как насчет использования агрегации для тегов?</p><ol> <li> <p>переименуйте свой класс <code>tagged</code> в <code>tags</code>.</p> </li> <li> <p>создайте интерфейс <code>tagged</code>:</p> <p>интерфейс tagged {теги gettags (); }</p> </li> <li> <p>пусть каждый класс, который должен быть "помечен", реализует <code>tagged</code> и пусть у него есть поле <code>tags</code>, которое возвращается из <code>gettags</code>.</p> </li> </ol><blockquote> <p><em> <strong>во-вторых, я не понимаю, как я мог бы изменить один из моих классов наследования на интерфейс. у меня есть переменные здесь, и они должны быть именно там.</strong> </em></p> </blockquote><p>это верно, интерфейсы не могут иметь переменных экземпляра. однако структуры данных, хранящие теги, не обязательно должны быть частью imo классов, которые помечены. выделите теги в отдельной структуре данных.</p>

<p>я бы решил это так: извлеките интерфейсы для классов <code>tagged</code> и <code>xmlelement</code> (возможно, вам не нужны все методы в открытом интерфейсе). затем реализуйте оба интерфейса, и реализующий класс имеет <code>tagged</code> (ваш фактический конкретный <code>tagged</code> класс) и <code>xmlelement</code> (ваш фактический конкретный <code>xmlelement</code> класс):</p><pre><code> 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(); } } </code></pre><hr><pre><code> public class taggedimpl implements tagged { @override public void sometaggedmethod() { // so what has to be done } } </code></pre><hr><pre><code> public interface tagged { public void sometaggedmethod(); } </code></pre><p>(и то же самое для xmlelement)</p>

<p>один из возможных способов;</p><p>1. вы можете создать базовый класс (ы) для общей функциональности, сделать его абстрактным, если вам не нужно создавать его экземпляр.</p><p>2. создайте интерфейсы и реализуйте эти интерфейсы в этих базовых классах. если требуется конкретная реализация, сделайте метод абстрактным. каждый конкретный класс может иметь свой собственный импл.</p><p>3 - расширить абстрактный базовый класс для конкретного класса (классов) и реализовать определенные интерфейсы на этом уровне</p>

<p>хорошо используя интерфейс и один базовый класс, вы просто заявляете:</p><ul>a) один объект может быть только одного типа (что верно в реальной жизни, если вы думаете, что голубь - это птица, тойота - это машина и т.д.). голубь - это тоже животное, но в любом случае каждая птица - животное, поэтому иерархически он находится над типом птицы. и в вашем oop дизайне. класс animal должен быть основой класса bird на случай, если вам нужно его представить -) и</ul><ul>б) может делать много разных вещей (птица может петь, может летать. машина может бежать, может остановиться и т.д.)</ul><p>в мире, где объекты могут быть нескольких типов (по горизонтали). допустим, дельфин - это млекопитающее, а также морское животное, в этом случае множественное наследование имело бы больше смысла. было бы проще представить его с использованием множественного наследования.</p>

<p>просто интересно, нельзя ли просто использовать внутренние (членские) классы (lrm 5.3.7)? например. вот так (основываясь на первом ответе выше):</p><pre><code>// 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 { // ... } } </code></pre><p>таким образом, у вас есть класс taggedxmlelement, который фактически содержит все элементы из двух исходных классов, а внутри taggedxmlelement у вас есть доступ к не закрытым членам классов-членов. конечно, нельзя было бы использовать "super", но вызывать методы класса-члена. в качестве альтернативы можно расширить один из классов и сделать другой класс-член. есть некоторые ограничения, но я думаю, что их можно обойти.</p>

<p>я работаю в аналогичной проблеме на android. мне нужно было расширить button и textview (оба наследуются от view) дополнительными функциями. из-за отсутствия доступа к их суперклассу, мне нужно было найти другое решение. я написал новый класс, который инкапсулирует все реализации:</p><pre><code>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(); } </code></pre><p>проблема здесь в том, что вашим классам нужен один и тот же супер класс. вы также можете попробовать использовать разные классы, но, например, проверьте, к какому типу они относятся.</p><pre><code>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 } } } </code></pre><p>итак, вот мой класс modifier, который инкапсулирует все реализации.</p><p>надеюсь, это кому-нибудь поможет.</p>

<p>использование композиции было бы способом, предложенным другим разработчиком. основным аргументом против множественного наследования является неоднозначность, создаваемая, когда вы расширяете два класса с одним и тем же объявлением метода (одно и то же имя метода и параметры). лично, однако, я думаю, что это куча дерьма. в этой ситуации легко может возникнуть ошибка компиляции, которая не сильно отличается от определения нескольких методов с одним и тем же именем в одном классе. что-то вроде следующего фрагмента кода может легко решить эту дилемму:</p><pre><code>public myextendedclass extends classa, classb { public duplicatemethodname() { return classa.duplicatemethodname(); } } </code></pre><p>другой аргумент против множественного наследования заключается в том, что java пытался упростить ситуацию, чтобы разработчики-любители не создавали сеть взаимозависимых классов, которая могла бы создать грязную, запутанную систему программного обеспечения. но, как вы видите в вашем случае, это также усложняет и запутывает вещи, когда они недоступны. кроме того, этот аргумент может быть использован для 100 других вещей в кодировании, поэтому у команд разработчиков есть обзоры кода, программное обеспечение для проверки стиля и ночные сборки.</p><p>тем не менее, в вашей конкретной ситуации вам придется согласиться с композицией (см. ответ шоджеи багини). он добавляет немного кода, но он эмулирует то же поведение, что и множественное наследование.</p>