Що таке міксин, і чому вони корисні?


953

У " Програмуванні Python " Марк Луц згадує "міксин". Я зі складу C / C ++ / C # і раніше не чула цього терміна. Що таке міксин?

Читаючи між рядками цього прикладу (з яким я пов’язаний, тому що він досить довгий), я припускаю, що це випадки використання множинної спадщини для розширення класу на відміну від "належного" підкласингу. Чи це правильно?

Чому я б хотів це зробити, а не ставити нову функціональність у підклас? З цього приводу, чому міксин / багаторазовий підхід до успадкування буде кращим, ніж використання композиції?

Що відокремлює міксин від багаторазового успадкування? Це просто питання семантики?

Відповіді:


709

Міксин - особливий вид багаторазового успадкування. Є дві основні ситуації, коли використовуються міксини:

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

Для прикладу номер один розглянемо систему запитів і відповідей werkzeug . Я можу зробити звичайний старий об’єкт запиту, сказавши:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

Якщо я хочу додати підтримку заголовка accept, я зробив би це

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

Якщо я хотів зробити об’єкт запиту, який підтримує заголовки, етаги, автентифікацію та підтримку агента користувача, я можу зробити це:

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

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

Різниця є тонкою, але у наведених вище прикладах класи міксіну не були прийняті самостійно. У традиційному багатократному успадкуванні AuthenticationMixin(наприклад), мабуть, буде щось подібне Authenticator. Тобто клас, мабуть, був розроблений так, щоб стояти самостійно.


123
Третя ситуація: ви хочете надати багато (необов'язкових) функцій для класу, але ви хочете, щоб функції були в окремих класах (і в окремих модулях), тому кожен модуль має приблизно одну особливість (поведінку.) IOW, не для повторного використання, але для компартменталізації.
bootchk

60
Напевно, це не проблема в цьому прикладі, але ви, як правило, хочете поставити основний базовий клас як останній елемент в круглих дужках, щоб створити ланцюжок успадкування: Request ==> Mixin ==> ... ==> BaseRequest. Дивіться тут: ianlewis.org/en/mixins-and-python
hillel

10
@hillel хороший момент, але майте на увазі, що Python буде називати методи суперклассів зліва направо (наприклад, якщо вам потрібно перекрити конструктор, наприклад).
Eliseu Monar dos Santos

9
Це звучить багато як візерунок дизайну Decorator.
D-Jones

4
4 - ая ситуація: є вже існуюче сімейство Parentкласу і Child1, Child2, ChildNпідкласи всередині 3 бібліотеки партії, і ви хочете налаштоване поведінку для всієї родини. В ідеалі ви хочете додати таку поведінку Parent, і сподіваємось, що розробник бібліотеки третьої сторони прийме ваш запит на витягнення. Інакше вам доведеться реалізувати свій власний class NewBehaviorMixin, а потім визначити повний набір класів обгортки, таких як class NewParent(NewBehaviorMixin, Parent): passі class NewChildN(NewBehaviorMixin, ChildN): passтощо. (PS: Чи знаєте ви кращий спосіб?)
RayLuo

240

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

В основному, міксин - це окремий базовий тип, який забезпечує обмежену функціональність та поліморфний резонанс для дитячого класу. Якщо ви думаєте на C #, подумайте про інтерфейс, який вам не потрібно реально реалізовувати, оскільки він уже реалізований; ви просто успадковуєте його і отримуєте користь від його функціональності.

Міксини, як правило, вузькі за обсягом і не повинні розширюватися.

[редагувати - щодо того, чому:]

Думаю, я повинен звернутися до цього питання, оскільки ви попросили. Велика перевага полягає в тому, що вам не доведеться робити це знову і знову. У C #, найбільше місце, де міксин може отримати користь, може бути від схеми утилізації . Щоразу, коли ви реалізуєте IDisposable, ви завжди завжди хочете дотримуватися тієї ж схеми, але ви закінчуєте писати та переписувати той самий базовий код з незначними варіаціями. Якщо був міксин для вивезення, який можна розширити, ви могли б заощадити багато додаткових текстів.

[редагувати 2 - відповісти на ваші інші запитання]

Що відокремлює міксин від багаторазового успадкування? Це просто питання семантики?

Так. Різниця між міксином та стандартним множинним успадкуванням - лише питання семантики; клас, який має багатократне успадкування, може використовувати міксин як частину цього багаторазового успадкування.

Суть міксину полягає в створенні типу, який можна "змішати" з будь-яким іншим типом шляхом успадкування, не впливаючи на тип успадкування, при цьому все ще пропонуючи певну корисну функціональність для цього типу.

Знову ж таки, подумайте про вже реалізований інтерфейс.

Я особисто не використовую комбінації, оскільки я розвиваюсь головним чином мовою, яка їх не підтримує, тому мені дуже важко придумати гідний приклад, який просто подасть це "аха!" момент для вас. Але я спробую ще раз. Я буду використовувати надуманий приклад - більшість мов уже так чи інакше надають цю функцію - але це, сподіваємось, пояснить, як міксини повинні створюватися та використовуватися. Ось:

Припустимо, у вас є тип, який ви хочете мати можливість серіалізувати до та з XML. Ви хочете, щоб тип надав метод "ToXML", який повертає рядок, що містить фрагмент XML зі значеннями даних типу, і "FromXML", що дозволяє типу реконструювати свої дані з фрагмента XML в рядку. Знову ж таки, це надуманий приклад, тому, можливо, ви використовуєте файловий потік або клас XML Writer з бібліотеки часу виконання вашої мови ... що завгодно. Справа в тому, що ви хочете серіалізувати свій об’єкт у XML та отримати новий об’єкт із XML.

Іншим важливим моментом у цьому прикладі є те, що ви хочете зробити це загальним способом. Вам не потрібно впроваджувати метод "ToXML" і "FromXML" для кожного типу, який ви хочете серіалізувати, ви хочете отримати деякі загальні засоби для того, щоб ваш тип зробив це, і він просто працює. Ви хочете повторно використовувати код.

Якщо ваша мова це підтримує, ви можете створити XmlSerializable mixin, щоб зробити вашу роботу за вас. Цей тип реалізує методи ToXML та FromXML. Використовуючи якийсь не важливий для прикладу механізм, він міг би зібрати всі необхідні дані з будь-якого типу, з яким він змішується, щоб створити фрагмент XML, повернутий ToXML, і він був би однаково здатний відновити ці дані, коли FromXML є називається.

І це все. Щоб використовувати його, у вас буде будь-який тип, який потрібно серіалізувати до спадщини XML від XmlSerializable. Щоразу, коли вам потрібно було серіалізувати або десеріалізувати цей тип, ви просто викликали б ToXML або FromXML. Насправді, оскільки XmlSerializable є повноцінним типом та поліморфним, ви могли б створити серіалізатор документів, який нічого не знає про ваш початковий тип, приймаючи, скажімо, масив типів XmlSerializable.

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

Якщо ви просто думаєте про міксин як про невеликий базовий тип, призначений для додавання невеликої кількості функціональності типу, не впливаючи інакше на цей тип, то ви золотий.

Сподіваємось. :)


25
Гей, чи подобається вам ця фраза "поліморфний резонанс"? Склав сам. Я думаю. Можливо, я десь почув це з фізики ...
Рандольфо

50
Я трохи не погоджуюся з вашим першим реченням. Ruby - це мова, що має єдину спадщину, і mixins - це спосіб додавання методів до заданого класу без успадкування з іншого класу.
Келтія

23
@Keltia: Я думаю, що міксин - за визначенням - множинне успадкування. У випадку Рубі - це мавпа (або щось інше), а не мікс. Люди Рубі можуть назвати це міксином, але це різна річ.
S.Lott

10
Насправді справжній міксин не може використовувати багаторазове успадкування. Міксин включає методи, атрибути тощо з одного класу в інший без успадкування. Це, як правило, дає переваги повторному використанню коду, здається, поліморфізмом, але залишає проблеми, що визначають батьківство (алмаз смерті та ін.) Мови, які підтримують Mixin, також, як правило, дозволяють часткове включення класу міксин (речі починають звучати трохи схоже на аспекти зараз).
Тревор

8
Для запису Java тепер підтримує комбінації з методами за замовчуванням.
шмосель

169

Ця відповідь має на меті пояснити міксин на прикладах :

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

  • на Python , а не на інших мовах.

    Зрозуміло, що були приклади з інших мов, таких як Рубі, оскільки термін набагато частіше зустрічається в цих мовах, але це нитка 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: багаторазове успадкування

Mixin - це модель дизайну, в якій деякий метод базового класу використовує метод, який він не визначає, і цей метод повинен бути реалізований іншим базовим класом , а не похідним, як у Визначенні 1.

Термін змішаний клас позначає базові класи, які призначені для використання у цій схемі проектування (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__ метод міксин безкоштовно.

Тому, принаймні, з цього пункту документації, міксин не потребує багаторазового успадкування і є узгодженим з Визначенням 1.

Документація, звичайно, може бути суперечливою в різних точках, і інші важливі бібліотеки Python можуть використовувати інше визначення у своїй документації.

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

Іншими мовами

  • Ruby: Очевидно, що не потрібно багаторазового успадкування для mixin, як згадується в основних довідниках, таких як програмування Ruby та The Programy Language Ruby

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

    Визначення 1 збігається з визначенням абстрактного класу (класу, який має чистий віртуальний метод). Цей клас неможливо ініціювати.

    Визначення 2 можливе при віртуальному успадкуванні: Множинне спадкування з двох похідних класів


37

Я вважаю їх дисциплінованим способом використання багаторазового успадкування - адже в кінцевому підсумку міксин - це лише ще один клас пітонів, який (можливо) слідує умовам про класи, які називаються міксинами.

Моє розуміння конвенцій, які регулюють те, що ви назвали б Mixin, - це те, що Mixin:

  • додає методи, але не змінні екземпляри (константи класу в порядку)
  • тільки успадковує від object (у Python)

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

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

Сказавши це, я бачив класи під назвою XYZMixin, у яких є змінні екземпляри.


30

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

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

Вікіпедія досі найкраща: http://en.wikipedia.org/wiki/Mixin


29

Що відокремлює міксин від багаторазового успадкування? Це просто питання семантики?

Міксин - це обмежена форма багаторазового успадкування. У деяких мовах механізм додавання міксину до класу дещо відрізняється (за синтаксисом) від механізму успадкування.

Особливо в контексті Python, mixin - це батьківський клас, який надає функціональні можливості підкласам, але не призначений для того, щоб самостійно інстанціювати.

Що може змусити вас сказати, "це просто багатократне успадкування, а не насправді міксин" - це якщо клас, який можна сплутати за міксин, насправді можна миттєво використати та використовувати - так що це справді семантична та дуже реальна різниця.

Приклад множинного спадкування

Цей приклад із документації - це OrdersCounter:

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інше призначене для інстанції та використання самостійно. Однак, підкласифікуючи їх обидва, ми можемо мати лічильник, який упорядкований і повторно використовувати код у кожному об'єкті.

Це потужний спосіб повторного використання коду, але це також може бути проблематично. Якщо виявиться, що в одному з об’єктів є помилка, виправлення без побоювання може створити помилку в підкласі.

Приклад міксину

Міксини, як правило, рекламуються як спосіб отримати повторне використання коду без потенційних проблем зв'язання, які можуть мати спільне множинне спадкування, як, наприклад, OrdersCounter. Коли ви використовуєте mixins, ви використовуєте функціональність, яка не так щільно поєднана з даними.

На відміну від наведеного вище прикладу, міксин не призначений для самостійного використання. Він забезпечує новий або інший функціонал.

Наприклад, у стандартній бібліотеці є кілька мікшин у socketserverбібліотеці .

Версії для розгортання та нанизування версій кожного типу серверів можна створити за допомогою цих класів змішування. Наприклад, 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, до речі, є особливим типом, який називається «Риса в Скалі».

Це натякає на спосіб визначення класу: клас NewClass розширює FirstMixin з SecondMixin з ThirdMixin ...

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


9

Я б радив не вводити мікси в новому коді Python, якщо ви можете знайти будь-який інший спосіб його вирішення (наприклад, композиція замість успадкування або просто методи виправлення мавп у ваші власні класи), що не набагато більше зусилля.

У класах старого стилю можна використовувати суміші як спосіб захоплення кількох методів з іншого класу. Але в світі нового стилю успадковується все, навіть суміш object. Це означає, що будь-яке використання багаторазового успадкування природно спричиняє проблеми МРО .

Є способи змусити MRO працювати з багатонаступництвом в Python, особливо функцію super (), але це означає, що вам потрібно виконати всю ієрархію класів за допомогою super (), і зрозуміти потік управління набагато складніше.


3
Оскільки у версії 2.3 Python використовує "роздільну здатність методу C3", пояснювану в наказі про дозвіл методу Python 2.3 або наказі про дозвіл методу .
webwurst

11
Особисто я б у більшості випадків брав міксини над лапками мавп; простіше міркувати і слідувати кодом.
tdammers

5
Захищений. Хоча у вашій відповіді висловлюється обґрунтована думка про стилі розробки, ви дійсно не вирішуєте власне питання.
Райан Б. Лінч

8

Можливо, пара прикладів допоможе.

Якщо ви будуєте клас і хочете, щоб він діяв як словник, ви можете визначити всі необхідні різні __ __методи. Але це трохи болить. Як альтернативу, ви можете просто визначити декілька та успадкувати (крім будь-якого іншого успадкування) від UserDict.DictMixin(переміщено collections.DictMixinв py3k). Це призведе до автоматичного визначення всіх рештів api словника.

Другий приклад: інструментарій графічного інтерфейсу wxPython дозволяє робити елементи управління списком з декількох стовпців (як, скажімо, відображення файлів у Windows Explorer). За замовчуванням ці списки досить основні. Ви можете додати додаткову функціональність, таку як можливість сортувати список за певним стовпцем, натиснувши на заголовок стовпця, успадкувавши від ListCtrl та додавши відповідні комбінації.


8

Це не приклад Python, але в мові програмування D термін mixinвикористовується для позначення конструкції, що використовується майже однаково; додавання до класу купу матеріалів.

У D (що, до речі, не робиться MI), це робиться шляхом вставки шаблону (подумайте синтаксично обізнані та безпечні макроси, і ви будете близькі) до області. Це дозволяє по одному рядку коду в класі, структурі, функції, модулі чи будь-якому іншому розширити до будь-якої кількості декларацій.


2
Mixin являє собою загальний термін, використовуваний в D, Ruby і т.д. Згідно Вікіпедії, вони виникли в старих системах шкільного LISP, і були вперше описані в 1983 році: en.wikipedia.org/wiki / ...
Lee B

7

ОП зазначив, що він / вона ніколи не чув про змішання в C ++, можливо, це тому, що їх називають цікаво повторюваним шаблоном шаблонів (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;
}

EDIT: Додано захищений конструктор у ComparableMixin, щоб він міг бути успадкованим, а не інстанційним. Оновлено приклад, щоб показати, як захищений конструктор буде викликати помилку компіляції, коли створюється об’єкт ComparableMixin.


Міксини та CRTP - це не одне й те саме в C ++.
ашрасмун

6

Можливо, приклад із рубіну може допомогти:

Ви можете включити Mixin Comparableі визначити одну функцію "<=>(other)", mixin забезпечує всі ці функції:

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

Це робиться шляхом виклику <=>(other) та повернення правильного результату.

"instance <=> other"повертає 0 , якщо обидва об'єкти рівні, менше 0 , якщо instanceбільше , ніж otherта більше , ніж 0 , якщо otherбільше.


Ось допис, що пропонує аналогічний mixin для Python. Хоча пропозиція визначає __lt__як базу замість __cmp__, останню з них насправді застаріло і не рекомендується використовувати. Мені здається , простіше у використанні , що підмішати замість досить складних декораторів (частина functools ) - хоча це один може бути в змозі реагувати більш динамічно на які надаються порівняння ...
Тобіас Kienzler

6

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
Яка різниця між цим та багаторазовим успадкуванням взагалі?
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

Різниця полягає в тому, що ви не в змозі створювати екземпляри з модулів, але якщо немає диференціації між загальними класами і модулями, змішання не є явною річчю, і важко зрозуміти, де загальний клас і де міксин
ka8725

Тож у Ruby mixins - це просто класи, які неможливо встановити, але їх слід використовувати для багаторазового успадкування?
Триларіон

6

Я щойно використав пітон-міксин, щоб здійснити тестування одиниць на мінометрів пітона. Зазвичай провідник розмовляє з МТА, що ускладнює тестування одиниць. Тестовий міксин переосмислює методи, які спілкуються з MTA, і створюють модельоване середовище, кероване тестовими кейсами.

Отже, ви берете немодифікований додаток, наприклад 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 у тестових випадках для застосування дози:

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

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

Наприклад, Python - це лише конвенції, оскільки все вищезазначене визначається як classes. Однак загальною рисою як абстрактних класів, інтерфейсів, так і MixIns є те, що вони не повинні існувати самостійно, тобто не повинні бути примірниками.


3

Я читав, що у вас є ac # background. Тож гарною відправною точкою може бути змішана реалізація для .NET.

Ви можете перевірити проект codeplex на веб-сайті http://remix.codeplex.com/

Перегляньте посилання lang.net Symposium, щоб отримати огляд. Доступна документація на сторінці codeplex.

з повагою до Стефана

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.