Почему включение модуля Ruby на самом деле не является «множественным наследованием» и как стиль Ruby позволяет избежать проблем, связанных с множественным наследованием?

Матц якобы сказал, что «миксины могут делать почти все, что делает множественное наследование, без связанных с этим недостатков» (слова Матца) ».

Прежде всего, почему включение модуля Ruby не является «множественным наследованием»? Мне кажется, что между модулями и классами очень мало различий. Тот факт, что вы не можете создать экземпляр модуля, не имеет значения, когда он используется в качестве суперкласса.

Я также знаю, что последующее включение модулей формирует единую цепочку наследования (не дерево), идущую вверх от класса. Но этого, на мой взгляд, недостаточно, чтобы отличить его от «множественного наследования», поскольку система множественного наследования Python также «линеаризует» цепочку суперклассов (используя алгоритм C3) просто процесс" линеаризации "Ruby значительно проще.

Так что же отличает миксины модулей Ruby от множественного наследования, скажем, в таком языке, как Python? И почему аргументы, лежащие в основе принятия Python алгоритма c3 MRO, не применимы к Ruby? И если они применимы - почему Ruby решила не применять этот алгоритм?

Благодарность


person horseyguy    schedule 27.10.2010    source источник
comment
Вот хорошая статья по этому поводу, посмотрите, ответит ли она на ваш вопрос: artima. ru / weblogs / viewpost.jsp? thread = 246488   -  person Mladen Jablanović    schedule 28.10.2010
comment
@Mladen, это просто великолепно .... если вы добавите это как правильный ответ, я "приму" его :)   -  person horseyguy    schedule 29.10.2010


Ответы (4)


Добавление этого от имени Младена в качестве фактического ответа, потому что я нашел его очень полезным, и я предполагаю, что ответы будут лучше проиндексированы для любых безумных вещей, которые SO делает с ним.

Вот хорошая статья по этому поводу, посмотрите, ответит ли она на ваш вопрос: http://artima.com/weblogs/viewpost.jsp?thread=246488 - Младен Ябланович 28 окт.

person A. Wilson    schedule 28.06.2012

С MI многие возникающие проблемы могут быть сведены к деталям реализации; нельзя просто говорить о «множественном наследовании» в целом, не говоря о конкретике. Поэтому я предполагаю, что вы имеете в виду «множественное наследование C ++», когда говорите «множественное наследование».

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

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

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

Я с уважением не согласен.

Во-первых, модули никогда не «используются как суперклассы» в ruby; есть только суперклассы.

Во-вторых, при множественном наследовании точное знание того, в каком порядке вызываются конструкторы (и деструкторы!), Составляет нетривиальный вопрос. Тот факт, что модули Ruby не позволяют создавать экземпляры, полностью устраняет эту проблему.

person kikito    schedule 07.02.2011
comment
Спасибо, однако лучшим сравнением был бы MI Python, а не C ++. Я снова с уважением не согласен с включенными модулями и «суперклассами» :). Включенный модуль работает почти так же, как «суперкласс» в Ruby - это указатель суперкласса класса указывает на включенный модуль (точно так же, как он указывает на суперкласс). Кроме того, тот факт, что модули Ruby следуют правилу, по которому всегда выигрывает последний включенный модуль, можно рассматривать просто как алгоритм линеаризации - аналогичный линеаризации c3 в Pythons - поэтому я не понимаю, как это само по себе отличает его от MI. - person horseyguy; 08.02.2011
comment
почти не то же самое, что и точно. Различия важны. Например, «супер» не работает для модулей. Но в любом случае я не понимал, что это вопрос Python. Думаю, я заслужил -1: / - person kikito; 08.02.2011
comment
super работает в модулях, но не работает для модулей просто потому, что модули нельзя разделить на подклассы. Модуль M может определять метод, для которого вызывается super, но он будет работать только в том случае, если класс, в который включен M, определяет super. В противном случае он будет неопределенным. - person kikito; 09.02.2011
comment
если у меня есть два модуля (A и B), а B включает A, я затем включаю B в класс C, создаю экземпляр и выполняю метод. Если этот метод определен на C, B, A со знаком «super», он вызовет каждый метод и поднимется по цепочке. В этом примере super отлично работает с модулями - вы специально сказали, что «супер не работает в модулях». Супер отлично работает в модулях. Я понятия не имею, что вы имеете в виду под «модулями нельзя разделить на подклассы» - в этом примере B фактически подклассы A, а подклассы C B. И super работает так, как и следовало ожидать от подкласса - он идет вверх по цепочке от C к B и А. - person horseyguy; 09.02.2011

Прочтите книгу «Метапрограммирование Ruby» из Pragmatic Press. Он содержит очень подробное объяснение этого в манере, которую очень легко читать и понимать. http://pragprog.com/titles/ppmetr/metaprogramming-ruby

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

class Foo
  include Bar
end

module Bar
end

foo = Foo.new

это создает цепочку наследования, где foo является экземпляром Foo, а Foo наследуется от Bar.

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

person Derick Bailey    schedule 27.10.2010
comment
Спасибо, хе-хе, но не могли бы вы подвести более подробный итог того, о чем говорится в книге? Примечание: мне не нужно знать особенности того, как вводится модуль, больше теории, объясняющей, почему они выбрали этот подход, а не типичный подход множественного наследования, и КАК он отличается от обычного подхода MI. :) Извините, что спрашиваю, но я не собираюсь покупать эту книгу (у меня уже слишком много, чтобы прочитать хе-хе). - person horseyguy; 27.10.2010
comment
Я прочитал «Метапрограммирование Ruby» и не знаю ответа на вопрос Банистера. - person Andrew Grimm; 29.10.2010

Самым важным в ruby ​​является то, что вы перезаписываете предыдущие определения данных / функций каждым включенным модулем.

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

Итак, пример кода для написания нового модуля (логически):

old_function = fun
fun = define_new_function
  do_you_staff
  call old_function

Вам не обязательно использовать старую функцию, хотя в большинстве случаев это полезно, но иногда проще переписать весь код.

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

Этот метод также называют исправлением обезьяны - термин, широко используемый в Ruby on Rails.

person Community    schedule 16.11.2010