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

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

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

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

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

спасибо

Ответ 1

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

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

Ответ 2

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

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

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

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

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

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

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

Ответ 3

Ознакомьтесь с книгой "Метапрограммирующий рубин" из прагматической прессы. Он содержит очень подробное объяснение этого, таким образом, что его очень легко читать и понимать. http://pragprog.com/titles/ppmetr/metaprogramming-ruby

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

class Foo
  include Bar
end

module Bar
end

foo = Foo.new

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

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

Ответ 4

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

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

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

old_function = fun
fun = define_new_function
  do_you_staff
  call old_function

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

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

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