Я только что прочитал об этой теме в «Хорошо обоснованном рубисте» ( кстати, отличная книга). Автор объясняет лучше, чем я, поэтому я процитирую его:
Ни одно правило или формула не всегда приводят к правильному дизайну. Но полезно иметь в виду пару соображений, когда вы принимаете решения относительно классов и модулей:
У модулей нет экземпляров. Отсюда следует, что сущности или вещи обычно лучше всего моделируются в классах, а характеристики или свойства сущностей или вещей лучше всего инкапсулировать в модулях. Соответственно, как отмечалось в разделе 4.1.1, имена классов обычно являются существительными, тогда как имена модулей часто являются прилагательными (Stack против Stacklike).
У класса может быть только один суперкласс, но он может смешивать столько модулей, сколько хочет. Если вы используете наследование, отдайте приоритет созданию разумных отношений суперкласс / подкласс. Не используйте одно-единственное отношение суперкласса для того, чтобы наделить класс тем, что может оказаться лишь одним из нескольких наборов характеристик.
Подводя итог этим правилам на одном примере, вот чего вам не следует делать:
module Vehicle
...
class SelfPropelling
...
class Truck < SelfPropelling
include Vehicle
...
Скорее вы должны сделать это:
module SelfPropelling
...
class Vehicle
include SelfPropelling
...
class Truck < Vehicle
...
Вторая версия моделирует сущности и свойства гораздо более аккуратно. Truck происходит от Vehicle (что имеет смысл), в то время как SelfPropelling является характеристикой транспортных средств (по крайней мере, всех тех, о которых мы заботимся в этой модели мира) - характеристикой, которая передается грузовикам в силу того, что Truck является потомком, или специализированная форма транспортного средства.
Truck
IS AVehicle
- нетTruck
того, что не было быVehicle
. Однако я бы назвал модуль возможноSelfPropelable
(:?) ХмSelfPropeled
звучит правильно, но это почти то же самое: D. Во всяком случае, я бы не стал включать его,Vehicle
но ...Truck
поскольку ЕСТЬ Транспортные средства, которых НЕТSelfPropeled
. Также хороший признак - спросить - А есть ли другие вещи, а НЕ автомобилиSelfPropeled
? - Ну, может быть, но найти будет труднее. Таким образом,Vehicle
можно наследовать от класса SelfPropelling (как класс он не подходит дляSelfPropeled
- поскольку это скорее роль)Я думаю, что миксины - отличная идея, но есть еще одна проблема, о которой никто не упомянул: коллизии пространств имен. Рассматривать:
Кто победит? В Ruby оказывается, что последний
module B
, потому что вы включили его послеmodule A
. Теперь этой проблемы легко избежать: убедитесь, что всеmodule A
иmodule B
«s константы и методы в неожиданных пространствах имен. Проблема в том, что компилятор вообще не предупреждает вас о столкновениях.Я утверждаю, что такое поведение не масштабируется для больших команд программистов - не следует предполагать, что реализующий человек
class C
знает обо всех именах в области видимости. Ruby даже позволит вам переопределить константу или метод другого типа . Я не уверен , что может когда - либо считать правильное поведение.источник
C#sayhi
выводитB::HELLO
не потому, что Ruby смешивает константы, а потому, что ruby разрешает константы от более близких к отдаленным, поэтомуHELLO
ссылка вB
всегда будет разрешатьB::HELLO
. Это справедливо, даже если класс CC::HELLO
тоже определил свой собственный .Мое мнение: модули предназначены для совместного использования, а классы - для моделирования отношений между объектами. Технически вы могли бы просто сделать все экземпляром Object и смешать любые модули, которые вы хотите получить, чтобы получить желаемый набор поведения, но это будет плохой, случайный и довольно нечитаемый дизайн.
источник
Ответ на ваш вопрос во многом зависит от контекста. Исходя из наблюдений pubb, выбор в первую очередь определяется рассматриваемой областью.
И да, ActiveRecord должен был быть включен, а не расширен подклассом. Другой ORM - datamapper - точно этого добивается!
источник
Мне очень нравится ответ Энди Гаскелла - просто хотел добавить, что да, ActiveRecord не должен использовать наследование, а скорее должен включать модуль для добавления поведения (в основном постоянства) к модели / классу. ActiveRecord просто использует неправильную парадигму.
По той же причине мне очень нравится MongoId, а не MongoMapper, поскольку он оставляет разработчику возможность использовать наследование как способ моделирования чего-то значимого в проблемной области.
Печально, что почти никто в сообществе Rails не использует «наследование Ruby» так, как оно должно использоваться - для определения иерархий классов, а не только для добавления поведения.
источник
Лучше всего я понимаю миксины как виртуальные классы. Миксины - это «виртуальные классы», которые были введены в цепочку предков класса или модуля.
Когда мы используем "include" и передаем ему модуль, он добавляет модуль в цепочку предков прямо перед классом, от которого мы наследуем:
Каждый объект в Ruby также имеет одноэлементный класс. Методы, добавленные к этому одноэлементному классу, могут вызываться непосредственно для объекта, поэтому они действуют как методы «класса». Когда мы используем "extension" для объекта и передаем объекту модуль, мы добавляем методы модуля к одноэлементному классу объекта:
Мы можем получить доступ к одноэлементному классу с помощью метода singleton_class:
Ruby предоставляет несколько ловушек для модулей, когда они смешиваются с классами / модулями.
included
- это метод-перехватчик, предоставляемый Ruby, который вызывается всякий раз, когда вы включаете модуль в какой-либо модуль или класс. Как и в случае с включенным, есть связанныйextended
крючок для расширения. Он будет вызываться, когда модуль расширяется другим модулем или классом.Это создает интересный паттерн, который могут использовать разработчики:
Как вы можете видеть, этот единственный модуль добавляет методы экземпляра, методы «класса» и действует непосредственно на целевой класс (в данном случае вызывая a_class_method ()).
ActiveSupport :: Concern инкапсулирует этот шаблон. Вот тот же модуль, переписанный для использования ActiveSupport :: Concern:
источник
Прямо сейчас я думаю о
template
шаблоне проектирования. Это было бы неправильно с модулем.источник