Что такое миксин и почему они полезны?

956

В « Программировании Python » Марк Лутц упоминает «миксины». Я из C / C ++ / C # фона, и я не слышал этот термин раньше. Что такое миксин?

Читая между строк этого примера (который я связал, потому что он довольно длинный), я предполагаю, что это случай использования множественного наследования для расширения класса, а не для «правильного» подкласса. Это правильно?

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

Что отличает миксин от множественного наследования? Это просто вопрос семантики?

TarkaDaal
источник

Ответы:

711

Миксин - это особый вид множественного наследования. Существуют две основные ситуации, в которых используются миксины:

  1. Вы хотите предоставить много дополнительных функций для класса.
  2. Вы хотите использовать одну особенность во множестве разных классов.

Для примера номер один рассмотрим систему запросов и ответов werkzeug . Я могу сделать простой старый объект запроса, сказав:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

Если я хочу добавить поддержку заголовка принять, я бы сделал это

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

Если бы я хотел создать объект запроса, который поддерживает заголовки принятия, etags, аутентификацию и поддержку агента пользователя, я мог бы сделать это:

from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin

class Request(AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin, BaseRequest):
    pass

Разница невелика, но в приведенных выше примерах классы mixin не были созданы для самостоятельной работы. В более традиционном множественном наследовании AuthenticationMixin(например), вероятно, будет что-то более похожее Authenticator. Таким образом, класс, вероятно, был бы разработан, чтобы стоять самостоятельно.

Джейсон Бейкер
источник
123
Третья ситуация: вы хотите предоставить много (не необязательных) функций для класса, но вы хотите, чтобы эти функции были в отдельных классах (и в отдельных модулях), чтобы каждый модуль имел одну функцию (поведение). для повторного использования, но для разделения.
bootchk
61
Вероятно, не проблема в этом примере, но вы обычно хотите поместить основной базовый класс в качестве последнего элемента в круглых скобках, чтобы создать цепочку наследования: Request ==> Mixin ==> ... ==> BaseRequest. Смотрите здесь: ianlewis.org/en/mixins-and-python
Гилель,
10
@hillel Хорошая мысль, но имейте в виду, что Python будет вызывать методы суперклассов слева направо (например, когда вам нужно переопределить конструктор).
Елисей Монар душ Сантуш
9
Это звучит очень похоже на шаблон дизайна Decorator.
Д-Джонс
4
4 - ая ситуация: есть уже существующее семейство Parentкласса и Child1, Child2, ChildNподклассы внутри 3 библиотеки партии, и вы хотите настроенное поведение для всей семьи. В идеале вы хотели бы добавить такое поведение Parentи надеяться, что сторонний разработчик библиотеки примет ваш запрос на извлечение. В противном случае вам придется реализовать свой собственный class NewBehaviorMixin, а затем определить полный набор классов- class NewParent(NewBehaviorMixin, Parent): passclass NewChildN(NewBehaviorMixin, ChildN): pass
оболочек,
240

Во-первых, вы должны заметить, что миксины существуют только в языках множественного наследования. Вы не можете сделать миксин в Java или C #.

По сути, миксин - это автономный базовый тип, который обеспечивает ограниченную функциональность и полиморфный резонанс для дочернего класса. Если вы думаете о C #, подумайте об интерфейсе, который вам не нужно реализовывать, потому что он уже реализован; вы просто наследуете его и извлекаете выгоду из его функциональности.

Миксины обычно узкие по объему и не предназначены для расширения.

[править - почему:]

Я полагаю, я должен ответить почему, так как вы спросили. Большим преимуществом является то, что вам не нужно делать это снова и снова. В C # самое большое место, где миксин мог бы извлечь выгоду, может быть из шаблона утилизации . Всякий раз, когда вы реализуете IDisposable, вы почти всегда хотите следовать одному и тому же шаблону, но в итоге вы пишете и переписываете один и тот же базовый код с небольшими изменениями. Если бы существовал расширяемый миксин Disposal, вы могли бы сэкономить много лишнего набора текста.

[править 2 - чтобы ответить на другие ваши вопросы]

Что отличает миксин от множественного наследования? Это просто вопрос семантики?

Да. Разница между миксином и стандартным множественным наследованием является лишь вопросом семантики; класс с множественным наследованием может использовать миксин как часть этого множественного наследования.

Смысл mixin состоит в том, чтобы создать тип, который можно «смешать» с любым другим типом посредством наследования, не затрагивая наследующий тип, и в то же время предлагать некоторые полезные функциональные возможности для этого типа.

Опять же, подумайте об интерфейсе, который уже реализован.

Лично я не использую миксины, так как я развиваюсь в основном на языке, который их не поддерживает, поэтому мне очень трудно придумать достойный пример, который просто поставит это «ахах!» момент для вас. Но я попробую еще раз. Я собираюсь использовать надуманный пример - большинство языков уже так или иначе предоставляют эту функцию - но, надеюсь, это объяснит, как миксины должны создаваться и использоваться. Поехали:

Предположим, у вас есть тип, который вы хотите иметь возможность сериализации в и из XML. Вы хотите, чтобы тип предоставил метод «ToXML», который возвращает строку, содержащую фрагмент XML со значениями данных типа, и «FromXML», который позволяет типу восстанавливать свои значения данных из фрагмента XML в строку. Опять же, это надуманный пример, поэтому, возможно, вы используете файловый поток или класс XML Writer из библиотеки времени выполнения вашего языка ... что угодно. Дело в том, что вы хотите сериализовать свой объект в XML и получить новый объект обратно из XML.

Другим важным моментом в этом примере является то, что вы хотите сделать это в общем виде. Вам не нужно реализовывать методы «ToXML» и «FromXML» для каждого типа, который вы хотите сериализовать, вам нужны некоторые общие средства, гарантирующие, что ваш тип будет делать это, и он просто работает. Вы хотите повторного использования кода.

Если ваш язык поддерживает это, вы можете создать миксин XmlSerializable, который сделает вашу работу за вас. Этот тип будет реализовывать методы ToXML и FromXML. Используя некоторый механизм, который не важен для примера, он мог бы собрать все необходимые данные из любого типа, с которым он смешан, для создания фрагмента XML, возвращаемого ToXML, и он был бы в равной степени способен восстанавливать эти данные, когда FromXML называется.

И это все. Чтобы использовать его, вы должны иметь любой тип, который нужно сериализовать, чтобы XML наследовал от XmlSerializable. Когда бы вам ни понадобилось сериализовать или десериализовать этот тип, вы просто вызывали бы ToXML или FromXML. Фактически, поскольку XmlSerializable является полноценным типом и полиморфным, вы могли бы создать сериализатор документов, который ничего не знает о вашем исходном типе, принимая только, скажем, массив типов XmlSerializable.

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

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

С надеждой. :)

Randolpho
источник
25
Эй, тебе нравится эта фраза "полиморфный резонанс"? Сделал это сам. Я думаю. Может быть, я где-то слышал это по физике ...
Рандольфо
50
Я немного не согласен с вашим первым предложением. Ruby - это язык с одним наследованием, а миксины - это способ добавления методов к данному классу без наследования от другого класса.
Keltia
23
@Keltia: я думаю, что mixin's - по определению - множественное наследование. В случае с Ruby, это обезьяна (или что-то еще), а не просто миксин. Ребята из Ruby могут назвать это миксином, но это разные вещи.
S.Lott
10
На самом деле, настоящий миксин не может использовать множественное наследование. Миксин включает методы, атрибуты и т. Д. Из одного класса в другой, не наследуя его. Это дает преимущества повторного использования кода при полиморфизме, но исключает проблемы, определяющие происхождение (алмаз смерти и т. Д.). Языки, поддерживающие миксин, также допускают частичное включение класса миксинов (вещи начинают звучать немного похоже на аспекты сейчас).
Тревор
8
Для записи, Java теперь поддерживает миксины с методами по умолчанию.
шмосел
170

Цель этого ответа - объяснить миксины на примерах :

  • Автономный : короткий, без необходимости знать какие-либо библиотеки, чтобы понять пример.

  • в Python , а не в других языках.

    Понятно, что были примеры из других языков, таких как Ruby, поскольку этот термин гораздо чаще встречается в этих языках, но это поток Python .

Он также рассмотрит спорный вопрос:

Многократное наследование необходимо или нет, чтобы характеризовать миксин?

Определения

Я до сих пор не видел цитату из «авторитетного» источника, в которой четко сказано, что такое миксин в Python.

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

Консенсус может отличаться для разных языков.

Определение 1: нет множественного наследования

Миксин - это такой класс, что некоторый метод класса использует метод, который не определен в классе.

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

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

Классическим примером является реализация всех операторов сравнения с только <=и ==:

class ComparableMixin(object):
    """This class has methods which use `<=` and `==`,
    but this class does NOT implement those methods."""
    def __ne__(self, other):
        return not (self == other)
    def __lt__(self, other):
        return self <= other and (self != other)
    def __gt__(self, other):
        return not self <= other
    def __ge__(self, other):
        return self == other or self > other

class Integer(ComparableMixin):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) <  Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) >  Integer(0)
assert Integer(1) >= Integer(1)

# It is possible to instantiate a mixin:
o = ComparableMixin()
# but one of its methods raise an exception:
#o != o 

Этот конкретный пример мог быть реализован через functools.total_ordering()декоратор, но игра здесь состояла в том, чтобы заново изобрести колесо:

import functools

@functools.total_ordering
class Integer(object):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) < Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) > Integer(0)
assert Integer(1) >= Integer(1)

Определение 2: множественное наследование

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

Термин класс mixin относится к базовым классам, которые предназначены для использования в этом шаблоне проектирования (TODO, которые используют метод, или те, которые его реализуют?)

Нелегко определить, является ли данный класс миксином или нет: метод может быть просто реализован в производном классе, и в этом случае мы вернемся к определению 1. Вы должны учитывать намерения автора.

Эта модель интересна тем, что можно комбинировать функции с различными вариантами выбора базовых классов:

class HasMethod1(object):
    def method(self):
        return 1

class HasMethod2(object):
    def method(self):
        return 2

class UsesMethod10(object):
    def usesMethod(self):
        return self.method() + 10

class UsesMethod20(object):
    def usesMethod(self):
        return self.method() + 20

class C1_10(HasMethod1, UsesMethod10): pass
class C1_20(HasMethod1, UsesMethod20): pass
class C2_10(HasMethod2, UsesMethod10): pass
class C2_20(HasMethod2, UsesMethod20): pass

assert C1_10().usesMethod() == 11
assert C1_20().usesMethod() == 21
assert C2_10().usesMethod() == 12
assert C2_20().usesMethod() == 22

# Nothing prevents implementing the method
# on the base class like in Definition 1:

class C3_10(UsesMethod10):
    def method(self):
        return 3

assert C3_10().usesMethod() == 13

Авторитетные появления Python

В официальной документации для collection.abc в документации явно используется термин Mixin Methods .

В нем говорится, что если класс:

  • инвентарь __next__
  • наследуется от одного класса Iterator

тогда класс получает __iter__ метод mixin бесплатно.

Поэтому, по крайней мере, в этом пункте документации, mixin не требует множественного наследования и согласуется с определением 1.

Разумеется, документация может быть противоречивой в разные моменты, и другие важные библиотеки Python могут использовать другое определение в своей документации.

На этой странице также используется термин Set mixin, который ясно указывает на то, что классы любят Setи Iteratorмогут называться классами Mixin.

На других языках

  • Ruby: Очевидно, что не требуется множественное наследование для mixin, как упоминалось в основных справочниках, таких как Programming Ruby и The Ruby Language Language.

  • C ++: метод, который не реализован, является чисто виртуальным методом.

    Определение 1 совпадает с определением абстрактного класса (класса, который имеет чисто виртуальный метод). Этот класс не может быть создан.

    Определение 2 возможно с виртуальным наследованием: множественное наследование от двух производных классов

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
источник
37

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

Мое понимание соглашений, которые управляют чем-то, что вы бы назвали миксином, таково, что миксин:

  • добавляет методы, но не переменные экземпляра (константы класса в порядке)
  • наследуется только от object(в Python)

Таким образом, он ограничивает потенциальную сложность множественного наследования и позволяет достаточно легко отслеживать поток вашей программы, ограничивая область поиска (по сравнению с полным множественным наследованием). Они похожи на модули ruby .

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

Сказав это, я видел классы с именем XYZMixin, которые имеют переменные экземпляра.

Хэмиш Даунер
источник
30

Mixins - это концепция в программировании, в которой класс предоставляет функциональные возможности, но он не предназначен для использования в экземплярах. Основная цель Mixins состоит в том, чтобы обеспечить функциональные возможности, которые являются автономными, и было бы лучше, если бы сами миксины не наследовали другие миксины и также избегали состояния. В таких языках, как Ruby, есть прямая языковая поддержка, но для Python - нет. Однако вы можете использовать наследование нескольких классов для выполнения функций, предоставляемых в Python.

Я смотрел это видео http://www.youtube.com/watch?v=v_uKI2NOLEM чтобы понять основы миксинов. Для новичка очень полезно понять основы миксинов и то, как они работают, и проблемы, с которыми вы можете столкнуться при их реализации.

Википедия по-прежнему лучшая: http://en.wikipedia.org/wiki/Mixin

lakesh
источник
29

Что отличает миксин от множественного наследования? Это просто вопрос семантики?

Mixin - это ограниченная форма множественного наследования. В некоторых языках механизм добавления mixin в класс немного отличается (с точки зрения синтаксиса) от механизма наследования.

В частности, в контексте Python, mixin - это родительский класс, который предоставляет функциональные возможности подклассам, но сам по себе не предназначен для реализации.

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

Пример множественного наследования

Этот пример из документации является OrderedCounter:

class OrderedCounter(Counter, OrderedDict):
     'Counter that remembers the order elements are first encountered'

     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

     def __reduce__(self):
         return self.__class__, (OrderedDict(self),)

Это подклассы и от Counterи OrderedDictот collectionsмодуля.

Оба Counterи OrderedDictпредназначены для реализации и использования самостоятельно. Однако, подклассифицируя их обоих, мы можем получить счетчик, который упорядочен и повторно использует код в каждом объекте.

Это мощный способ повторного использования кода, но он также может быть проблематичным. Если окажется, что в одном из объектов есть ошибка, ее исправление может привести к ошибке в подклассе.

Пример миксина

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

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

Например, в стандартной библиотеке есть пара миксинов в socketserverбиблиотеке .

Версии Forking и Threading для каждого типа сервера могут быть созданы с помощью этих смешанных классов. Например, ThreadingUDPServer создается следующим образом:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

Первым является смешанный класс, так как он переопределяет метод, определенный в UDPServer. Установка различных атрибутов также меняет поведение базового серверного механизма.

В этом случае методы mixin переопределяют методы в UDPServerопределении объекта, чтобы обеспечить параллелизм.

Переопределяется метод , как представляется, process_requestи он также предоставляет другой метод, process_request_thread. Вот это из исходного кода :

class ThreadingMixIn:
        """Mix-in class to handle each request in a new thread."""

        # Decides how threads will act upon termination of the
        # main process
        daemon_threads = False

        def process_request_thread(self, request, client_address):
            """Same as in BaseServer but as a thread.
            In addition, exception handling is done here.
            """
            try:
                self.finish_request(request, client_address)
            except Exception:
                self.handle_error(request, client_address)
            finally:
                self.shutdown_request(request)

        def process_request(self, request, client_address):
            """Start a new thread to process the request."""
            t = threading.Thread(target = self.process_request_thread,
                                 args = (request, client_address))
            t.daemon = self.daemon_threads
            t.start()

Придуманный пример

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

class SimpleInitReprMixin(object):
    """mixin, don't instantiate - useful for classes instantiable
    by keyword arguments to their __init__ method.
    """
    __slots__ = () # allow subclasses to use __slots__ to prevent __dict__
    def __repr__(self):
        kwarg_strings = []
        d = getattr(self, '__dict__', None)
        if d is not None:
            for k, v in d.items():
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        slots = getattr(self, '__slots__', None)
        if slots is not None:
            for k in slots:
                v = getattr(self, k, None)
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        return '{name}({kwargs})'.format(
          name=type(self).__name__,
          kwargs=', '.join(kwarg_strings)
          )

и использование будет:

class Foo(SimpleInitReprMixin): # add other mixins and/or extend another class here
    __slots__ = 'foo',
    def __init__(self, foo=None):
        self.foo = foo
        super(Foo, self).__init__()

И использование:

>>> f1 = Foo('bar')
>>> f2 = Foo()
>>> f1
Foo(foo='bar')
>>> f2
Foo(foo=None)
Аарон Холл
источник
11

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

В Scala вы можете создавать миксины, как описано здесь, но что очень интересно, это то, что миксины фактически «слиты» вместе, чтобы создать новый тип класса для наследования. По сути, вы не наследуете от нескольких классов / миксинов, а генерируете новый тип классов со всеми свойствами миксина для наследования. Это имеет смысл, поскольку Scala основана на JVM, где множественное наследование в настоящее время не поддерживается (начиная с Java 8). Этот тип класса mixin, кстати, является специальным типом, называемым Trait в Scala.

На это намекает способ определения класса: класс NewClass расширяет FirstMixin с помощью SecondMixin с помощью ThirdMixin ...

Я не уверен, что интерпретатор CPython делает то же самое (миксин-класс-композиция), но я не удивлюсь. Кроме того, исходя из фона C ++, я бы не назвал ABC или «интерфейс» эквивалентом mixin - это похожая концепция, но расходящаяся в использовании и реализации.

SilentDirge
источник
9

Я бы посоветовал не смешивать модули в новом коде Python, если вы можете найти какой-либо другой способ обойти это (например, метод «составление вместо наследования» или просто методы «залатывания обезьянами» в ваших собственных классах), который не намного больше усилия.

В классах старого стиля вы можете использовать дополнения как способ получения нескольких методов из другого класса. Но в мире нового стиля все, даже смешивание, наследуется от object. Это означает, что любое использование множественного наследования естественно создает проблемы MRO .

Существуют способы заставить MRO с множественным наследованием работать в Python, в частности, функцию super (), но это означает, что вы должны выполнить всю иерархию классов, используя super (), и понять процесс управления значительно сложнее.

bobince
источник
3
Начиная с версии 2.3, Python использует «разрешение метода C3», описанное в Порядке разрешения метода Python 2.3 или Порядке разрешения метода .
webwurst
11
Лично я бы в большинстве случаев принимал миксины вместо исправлений обезьян; Проще рассуждать и следовать коду.
tdammers
5
Downvoted. Хотя ваш ответ выражает правильное мнение о стилях разработки, вы на самом деле не отвечаете на настоящий вопрос.
Райан Б. Линч
8

Возможно, пара примеров поможет.

Если вы создаете класс и хотите, чтобы он действовал как словарь, вы можете определить все __ __необходимые методы. Но это немного больно. В качестве альтернативы, вы можете просто определить несколько и наследовать (в дополнение к любому другому наследованию) от UserDict.DictMixin(перемещено в collections.DictMixinв py3k). Это приведет к автоматическому определению всех остальных словаря API.

Второй пример: инструментарий GUI wxPython позволяет создавать элементы управления списком с несколькими столбцами (как, например, отображение файла в проводнике Windows). По умолчанию эти списки являются довольно простыми. Вы можете добавить дополнительные функции, такие как возможность сортировки списка по определенному столбцу, щелкнув заголовок столбца, наследуя от ListCtrl и добавив соответствующие миксины.

Джон Фухи
источник
8

Это не пример Python, но в языке программирования D этот термин mixinиспользуется для обозначения конструкции, используемой почти таким же образом; добавив кучу вещей в класс.

В D (который, кстати, не делает MI) это делается путем вставки шаблона (подумайте о синтаксически осведомленных и безопасных макросах, и вы будете близки) в область видимости. Это позволяет использовать одну строку кода в классе, структуре, функции, модуле или любом другом объекте для расширения до любого количества объявлений.

BCS
источник
2
Mixin - это общий термин, используемый в D, Ruby и т. Д. Согласно Википедии, они возникли в старых школьных lisp-системах и были впервые задокументированы в 1983 году: en.wikipedia.org/wiki/…
Lee B
7

OP упомянул, что он / она никогда не слышал о mixin в C ++, возможно, потому, что они называются Curily Recurring Template Pattern (CRTP) в C ++. Также @Ciro Santilli отметил, что mixin реализован через абстрактный базовый класс в C ++. Хотя абстрактный базовый класс может быть использован для реализации mixin, это избыточное решение, поскольку функциональность виртуальной функции во время выполнения может быть достигнута с использованием шаблона во время компиляции без затрат на поиск виртуальной таблицы во время выполнения.

Шаблон CRTP подробно описан здесь

Я преобразовал пример python в ответе @Ciro Santilli в C ++, используя шаблонный класс ниже:

    #include <iostream>
    #include <assert.h>

    template <class T>
    class ComparableMixin {
    public:
        bool operator !=(ComparableMixin &other) {
            return ~(*static_cast<T*>(this) == static_cast<T&>(other));
        }
        bool operator <(ComparableMixin &other) {
            return ((*(this) != other) && (*static_cast<T*>(this) <= static_cast<T&>(other)));
        }
        bool operator >(ComparableMixin &other) {
            return ~(*static_cast<T*>(this) <= static_cast<T&>(other));
        }
        bool operator >=(ComparableMixin &other) {
            return ((*static_cast<T*>(this) == static_cast<T&>(other)) || (*(this) > other));
        }
        protected:
            ComparableMixin() {}
    };

    class Integer: public ComparableMixin<Integer> {
    public:
     Integer(int i) {
         this->i = i;
     }
     int i;
     bool operator <=(Integer &other) {
         return (this->i <= other.i);
     }
     bool operator ==(Integer &other) {
         return (this->i == other.i);
     }
    };

int main() {

    Integer i(0) ;
    Integer j(1) ;
    //ComparableMixin<Integer> c; // this will cause compilation error because constructor is protected.
    assert (i < j );
    assert (i != j);
    assert (j >  i);
    assert (j >= i);

    return 0;
}

РЕДАКТИРОВАТЬ: Добавлен защищенный конструктор в ComparableMixin, так что он может быть только унаследован и не создан. Обновлен пример, чтобы показать, как защищенный конструктор будет вызывать ошибку компиляции при создании объекта ComparableMixin.

bigdata2
источник
Mixins и CRTP - это не одно и то же в C ++.
ашрасмун
6

Может быть, пример из ruby ​​может помочь:

Вы можете включить миксин Comparableи определить одну функцию "<=>(other)", миксин предоставляет все эти функции:

<(other)
>(other)
==(other)
<=(other)
>=(other)
between?(other)

Он делает это, вызывая <=>(other)и возвращая правильный результат.

"instance <=> other"возвращает 0, если оба объекта равны, меньше 0, если instanceбольше, otherи больше 0, если otherбольше.

Георг Шолли
источник
Вот пост, предоставляющий похожий миксин для Python. Хотя предположение определяется __lt__как базовое, а не __cmp__последнее, которое фактически не рекомендуется и не рекомендуется использовать. Мне кажется, проще использовать этот миксин вместо довольно сложных декораторов (часть functools ) - хотя этот может быть в состоянии более динамично реагировать на то, какие сравнения даются ...
Тобиас Кинцлер,
6

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

Вот пример, который объясняет, как множественное наследование достигается с помощью mixin.

module A    # you create a module
    def a1  # lets have a method 'a1' in it
    end
    def a2  # Another method 'a2'
    end
end

module B    # let's say we have another module
    def b1  # A method 'b1'
    end
    def b2  #another method b2
    end
end

class Sample    # we create a class 'Sample'
    include A   # including module 'A' in the class 'Sample' (mixin)
    include B   # including module B as well

    def S1      #class 'Sample' contains a method 's1'
    end
end

samp = Sample.new    # creating an instance object 'samp'

# we can access methods from module A and B in our class(power of mixin)

samp.a1     # accessing method 'a1' from module A
samp.a2     # accessing method 'a2' from module A
samp.b1     # accessing method 'b1' from module B
samp.b2     # accessing method 'a2' from module B
samp.s1     # accessing method 's1' inside the class Sample
Акаш Соти
источник
4
В чем разница между этим и множественным наследованием в целом?
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
Разница в том, что вы не можете создавать экземпляры из модулей, но если нет различий между общими классами и модулями, миксины не являются явными вещами, и трудно понять, где находится общий класс, а где миксин
миксин ka8725
Таким образом, в Ruby mixins - это просто классы, которые не могут быть созданы, но должны использоваться для множественного наследования?
Триларион,
6

Я просто использовал Python Mixin для реализации модульного тестирования Python Milters. Как правило, milter разговаривает с MTA, что затрудняет юнит-тестирование. Тестовый миксин переопределяет методы, которые общаются с MTA, и вместо этого создает моделируемую среду, управляемую тестовыми примерами.

Итак, вы берете немодифицированное приложение milter, такое как spfmilter, и mixin TestBase, например:

class TestMilter(TestBase,spfmilter.spfMilter):
  def __init__(self):
    TestBase.__init__(self)
    spfmilter.config = spfmilter.Config()
    spfmilter.config.access_file = 'test/access.db'
    spfmilter.spfMilter.__init__(self)

Затем используйте TestMilter в тестовых случаях для приложения milter:

def testPass(self):
  milter = TestMilter()
  rc = milter.connect('mail.example.com',ip='192.0.2.1')
  self.assertEqual(rc,Milter.CONTINUE)
  rc = milter.feedMsg('test1',sender='good@example.com')
  self.assertEqual(rc,Milter.CONTINUE)
  milter.close()

http://pymilter.cvs.sourceforge.net/viewvc/pymilter/pymilter/Milter/test.py?revision=1.6&view=markup

Стюарт Гатман
источник
4

Я думаю, что предыдущие ответы очень хорошо определили, что такое MixIns . Однако, чтобы лучше понять их, было бы полезно сравнить MixIns с абстрактными классами и интерфейсами с точки зрения кода / реализации:

1. Абстрактный класс

  • Класс, который должен содержать один или несколько абстрактных методов

  • Абстрактный класс может содержать состояние (переменные экземпляра) и неабстрактные методы

2. Интерфейс

  • Интерфейс содержит только абстрактные методы (без неабстрактных методов и без внутреннего состояния)

3. Смешивание

  • MixIns (как и Interfaces) не содержат внутреннего состояния (переменные экземпляра)
  • MixIns содержат один или несколько неабстрактных методов (они могут содержать неабстрактные методы в отличие от интерфейсов)

Например, в Python это просто соглашения, потому что все вышеперечисленное определено как classes. Тем не менее, общая особенность абстрактных классов, интерфейсов и MixIns заключается в том, что они не должны существовать сами по себе, т.е. не должны создаваться.

Томаш Бартковяк
источник
3

Я читал, что у вас есть фон AC #. Так что хорошей отправной точкой может быть смешанная реализация для .NET.

Возможно, вы захотите проверить проект codeplex на http://remix.codeplex.com/

Смотрите ссылку на симпозиум lang.net, чтобы получить обзор. Еще больше информации о документации можно найти на странице кодекса.

С уважением Стефан

Стефан Папп
источник