что значит миксины в манге
Миксины
Царство: | Животные |
Тип: | Хордовые |
Класс: | Круглоротые |
Отряд: | Миксинообразные |
Семейство: | Миксиновые |
Микси́новые (лат. Myxinidae ) — семейство позвоночных класса круглоротых. Длина тела — 45—70 см. Непарная ноздря расположена на конце головы и сообщается с глоткой. Рот и ноздря обрамлены 6—8 мясистыми усиками. Жаберных мешков — 5—15 пар; у одних видов каждый мешок сообщается с глоткой и наружной средой, у других — они открываются с каждой стороны общим отверстием. Жаберный скелет состоит из небольшого числа хрящевых пластинок. Кровеносная система незамкнутая, имеется основное сердце и 3 дополнительных. Глаза затянуты кожей; светочувствительные клетки располагаются также вокруг клоаки. Один отряд с 1 семейством (около 15 видов); распространены в умеренных и субтропических водах Мирового океана. Откладывают 20—30 крупных овальных яиц (размером 18—20 мм). В России европейская миксина (или миксина обыкновенная, Myxine glutinosa) изредка встречается в Баренцевом море. Миксины — хищники, выедают внутренности и мышцы у ослабевших рыб, вгрызаясь в жертву с помощью мощного языка с роговыми зубцами; реже питаются червями. Производят огромное количество слизи на поверхности тела. Будучи схваченными, например за хвост, миксины способны завязываться в узел, которым они упираются в нападающего и перемещая узел вдоль тела к хвосту, а также благодаря выделяемой слизи, высвобождаются из захвата. Очень живучи, хорошо переносят длительное пребывание без воды, могут подолгу голодать и долго остаются живыми, получив даже чрезвычайно тяжелые ранения. Описан случай, когда она продолжала плавать через пять часов после того, как была обезглавлена. Миксины для “классов” в JavaScriptОдинаковый код в нескольких местах — это боль. Сегодня я напишу пару слов про повторяющиеся куски классов. Люди давно придумали решение — можно вынести одинаковые методы и свойства в общий базовый класс, а если такового нет — использовать примеси. Существует миллион реализаций данного паттерна для JavaScript, я хочу детально остановиться на подходе, когда миксин попадает в цепочку наследования. Проблема в картинкахНачнем с визуализации нашей проблемы. Допустим у нас есть два базовых класса и от них наследуются два дочерних класса. В какой-то момент в дочерних классах появляется необходимость в одинаковом функционале. Обычная копипаста будет выглядеть на нашей схеме вот так: Очень часто бывает, что данный функционал не имеет ничего общего с родительскими классами, поэтому выносить его в какой-то базовый класс нелогично и неправильно. Вынесем его в отдельное место — миксин. С точки зрения языка миксин может быть обычным объектом. А теперь обсудим момент, ради которого написана вся статья — как правильно замешивать наш миксин в классы. Исходя из собственного опыта, могу заявить, что самый удобный способ — это создание временного класса на основе миксина и подстановка его в очередь наследования. Плюсы данного подходаПишем кодВо всех последующих примерах будет использоваться конкретная реализация — библиотека Backbone.Mix. Посмотрев код, вы обнаружите, что он чрезвычайно прост, поэтому вы можете легко адаптировать его для своего любимого фреймворка. Давайте посмотрим, как применять миксины, встраивающиеся в цепочку наследования, в реальной жизни и прочувствуем плюсы данного подхода на практике. Представьте, что вы пишете сайт )) и на вашем сайте есть разные штуки, которые можно закрывать — попапы, хинты и т.п. Все они должны слушать клик по элементу с CSS классом close и скрывать элемент. Миксин для этого может выглядеть так: Вмешиваемся.Довольно просто, не правда ли? Теперь наша цепочка наследования выглядит так: Здесь и далее в примерах используется библиотека backbone-super Примеси, которые не мешают..… а помогают. Бывает замес выходит не хилый, и одним миксином не обойтись. Например, представьте что мы — крутые пацаны, и пишем лог в IndexedDB, а еще у нас для этого свой миксин — Loggable 🙂 Тогда к попапу мы будем мешать уже два миксина: Синтаксис вроде не сложный. На схеме это будет выглядеть так: Как видите, цепочка наследования выстроится в зависимости от порядка подключения миксинов. Зависимые миксиныА теперь представьте ситуацию, что к нам подходит наш аналитик и сообщает, что хочет собирать статистику по всем закрытиям попапов, хинтов — всего, что может закрываться. Конечно же, у нас давно есть миксин Trackable для таких случаев, с того времени, как мы делали регистрацию на сайте. И в цепочке наследования Trackable должен оказаться раньше, чем Closable : Код для миксинов с зависимостями немного усложнится: Документируй миксины правильноНа этом, пожалуй всё, счастливого вмешивания! Словарь анимешника (^_^)
Цвет kirei [кИрэй] золотой Природа akari [акари] луч света hana [хана] цветок, лепесток, перо hikari [хикари] свет kaze [казэ] ветер м.б. простуда tsubasa [цубаса] крылья Животные kairu [кайру] лягушка kotori [котори] птица wau-chan [ваучан] щенок Люди anata / anta [аната / анта] ты дорогой aniki [аники] старший брат baka [бака] дурачок (необидное ругательство) boku [боку] я (местоимение), я (используемое мальчиками). Иногда используется и девочками, по видимому, кто как себя позиционирует futari [футари] оба, двое hime [химе] принцесса imouto [имото] младший kareshi kanojo [кареш канодзё] парень и девушка (пара) kodomo [кодомо] ребенок minna [мина] все (обращение к группе) o [оо] ваше величество ohaoe [охаое] мама (необычное обращение). Впервые услышано в Air okusama [оксама] госпожа onee-chan [онэ-чан] сестренка onii-chan [онии-чан] братик ototo [отото] младший брат saichou [сайчо] президент (председатель) sempai [семпай] старший по работе или учебе sensei [сенсей] учитель, преподаватель любой shoujo [сёдзё] девочка shounen [сёнен] мальчик tabibito [табибито] путешественник tomodachi [томодачи] друзья tonari [тонари] соседи ware-ware [варе-варе] мы (о группе) watashi / atashi [ваташи] я (местоимение), я (используемое девочками) Тело kokoro [кокоро] сердце Одежда mizugi [мизуги] купальник wanpiisu [ванпис] платье pantsu [панцу] трусики yukata [юката] праздничное кимоно gohan [гохан] рис, еда любая ichigo [ичиго] клубника itadakimasu [итадакимас] приступим, приятного аппетита kampai [кампай] до дна (произносится вместо тоста) nabe [набэ] кастрюля или блюдо, которое готовится в кастрюле obento [обэнто] обед в коробочке okaeri [окари] еще, добавки onigiri [онигири] рисовые колобки pico-pico [пико-пико] хочется есть takoyaki [такояки] вид выпечки tamago [тамаго] яйцо tayaki [таяки] вид выпечки tofu [тофу] соевый творог Выражение чувств и эмоций hazukoshi [хазукоши] смущать hidoi [хидои] вредный (о человеке) kakkoii [какои] красивый kawai [каваи] симпатичный, хоршенький kirai [кирай] не нравится kirei [кирей] красивый kowai [ковай] страшно moshkashte [мошкаште] неужели, сомнение nozomu [нозому] желание samri [самри] холодно (о температуре) sateto [сатето] ну что же (готовность начать что либо) steki [стэки] прекрасный sugoi [сугой] круто восхищение ureshi [уреши] рад, радость yappari [йаппари] определенно, так и знал (подтверждение догадки) yokatta [йокатта] обошлось, слава богу Мистика akuma [акума] дьявол fushigi [фушиги] таинственный, мистический mahou [махо] магия, маг, волшебник, волшебство, чудо obakeshki [обакешки] дом с привидениями (развлечение на фестивале) Измерения zutto [зутто] давно всегда hanbun [ханбун] половина konoha [коноха] весна ksetsne [ксэцнэ] сезон mainichi [майничи] каждый день okina [окина] большой tsugi [цуги] следующий Мероприятия matsumari [мацумари] фестиваль sampo [сампо] прогулка ukutsu [укуцу] тренировка Общение arigatou [аригато] спасибо betsu ni [бэц ни] ничего chotto ii [чотто ии] есть минутка? (просьба отвлечься) domou arigatou [домо аригато] большое спасибо doumo [домо] благодарю douzo [дозо] пожалуйста (разрешаю что либо сделать) dzen dzen [дзен дзен] вовсе, совсем нет gomen nasai [гоменнасай] прошу прощения hontou [хонто] правда? неужели? ino [ино] а можно? (вежливый вопрос на предложение что-либо сделать) itte irasai [иттэрасай] возвращайся поскорей konbanwa [конбава] добрый вечер konnichiwa [коннитива] добрый день masaka [масака] не может быть mite [митэ] смотри! (привлечь внимание) nani [нани] чего? (вопрос) naruhodo [нарухото] конечно ясно ohaio [охайо] доброе утро okaeri nasai [окаринасэй] добро пожаловать onegai [онэгай] пожалуйста прошу oyasumi [ойасми] спокойной ночи sayonara [сайонара] прощай so, soka [со], [сока] вот как, понятность tadaima [тадайма] я дома (при возвращении) uso [усо] ложь, неправда wakanai [ваканай] не знаю wakatta [ваката] понятно yakusoku [йаксок] обещание yokoso [йокосо] добро пожаловать приветствие yubikiri [юбикири] скрепление обещания Разное baito [байто] работа daijoubu [дайджёбу] все в порядке demo [дэмо] но (предлог) fuafua [фуафуа] мягкий ganbatte [гамбатте] постарайся, борись haruka [харука] далеко hayaku [хаяку] быстрее hikoki [хикоки] самолет, летательный аппарат (например, дирижабль) himitsu [химицу] секрет ich, ni, sam, chi [ич, ни, сам, чи] один, два, три, четыре (счет) kenchi [кенши] запрещено kuso [ксо] черт! (ругательство) mamoru [мамору] защищать matte [матэ] подожди moshi-moshi [моши-моши] алло (ответ по телефону) o furo [офуро] ванна oboite [обойте] помнить shiteru [штэру] знать tasukete [таскетэ] спасти (просьба о помощи) Множественное наследование и миксины в PythonНедавно я пересмотрел три своих старых статьи о представлениях на основе классов Django (class-based views), которые написал для своего блога, обновив их до Django 3.0 (вы можете найти их здесь), и еще раз обнаружил, большое количество кода использующего классы mixin для улучшения повторного использования кода. По своему опыту я понял, что миксины не очень популярны в Python, поэтому решил изучить их лучше, тем самым освежив свои знания теории ООП. Чтобы полностью оценить содержание поста, убедитесь, что вы усвоили два столпа ООП: делегирование, в частности, как оно реализуется посредством наследования, и полиморфизм. Этот пост подробнее расскажет о делегировании а этот пост о полиморфизме. Множестенное наследование: благодать и проклятьеОбщая концепцияЧтобы обсудить миксины, мы должны начать с одного из самых спорных вопросов в мире ООП: множественное наследование. Это естественное расширение концепции простого наследования, когда класс автоматически делегирует разрешенные метода и атрибуты другому классу (от родительского классу к дочернему). Позвольте мне заявить еще раз, поскольку это важно для остальной части обсуждения: наследование — это просто механизм автоматического делегирования. Делегирование было введено в ООП как способ уменьшить дублирование кода. Когда объекту нужна определенная функция, он просто делегирует ее другому классу (явно или неявно), поэтому код пишется только один раз. Давайте рассмотрим пример сайта управления кодом (совершенно вымышленный и не вдохновленный каким-либо существующим продуктом). Давайте предположим, что мы создали следующую иерархию которая позволяет нам помещать в pull request только определенный код, требуемый этим элементом. Вызовы методов и делегирование — это не что иное, как сообщения между объектами, поэтому иерархия делегирования — это простая сетевая система. К сожалению, использование наследования вместо композиции часто приводит к системам, которые, как это ни парадоксально, увеличивают дублирование кода. Основная проблема заключается в том, что наследование может напрямую делегироваться только одному классу (родительскому классу), в отличие от композиции, где объект может делегировать любому количеству других классов. Это ограничение наследования означает, что у нас может быть класс, который наследует от другого, потому что ему нужны некоторые его функции, но при этом так же получает все другие функции, которые он не хочет или не должен иметь. Давайте продолжим пример портала управления кодом и рассмотрим проблему, которая представляет собой элемент, который мы хотим сохранить в системе, но который пользователь не может просмотреть. Если мы создадим иерархию, как это в итоге мы помещаем функции, связанные с процессом проверки, в объект, который не должен иметь их. Стандартное решение этой проблемы состоит в том, чтобы увеличить глубину иерархии наследования и получать их от нового более простого предка. Тем не менее, этот подход перестает быть жизнеспособным, как только объект должен наследовать от данного класса, но не от родителя этого класса. Например, элемент, который должен быть рецензируемым, но не назначаемым, например, best practice, которую мы хотим добавить на сайт. Если мы хотим продолжать использовать наследование, единственным решением на этом этапе является дублирование кода, который реализует рецензируемую природу элемента (или код, который реализует назначаемую функцию) и создание двух разных иерархий классов. Обратите внимание, что это даже не принимает во внимание, что новый reviewable item может нуждаться в атрибутах assignable item, что требует другого уровня глубины в иерархии, где мы выделяем эти функции в более общем классе. Так что, к сожалению, есть вероятность, что это только первый из многих компромиссов, которые нам придется принять, чтобы сохранить систему в стабильном состоянии, если мы не сможем изменить наш подход. Вышеупомянутая ситуация может быть затем решена с помощью pull request наследования как от класса, который предоставляет функции assign, так и от класса, который реализует функции reviewable. Вообще говоря, множественное наследование вводится, чтобы дать программисту возможность продолжать использовать наследование без дублирования кода, сохраняя иерархию классов проще и чище. В конце концов, все, что мы делаем в разработке программного обеспечения, это пытаемся разделить задачи, то есть изолировать функции, и множественное наследование может помочь сделать это. Это всего лишь примеры, которые могут быть действительными или нет, в зависимости от конкретного случая, но они ясно показывают проблемы, которые могут возникнуть у нас даже при очень простой иерархии из 4 классов. Многие из этих проблем явно возникают из-за того, что мы хотели реализовать делегирование только посредством наследования, и я осмелюсь сказать, что 80% архитектурных ошибок в проектах ООП происходят из-за использования наследования вместо композиции и использования God объектов(божественных объектов), то есть классов, которые несут ответственность за слишком много различных частей системы. Всегда помните, что ООП родился с идеей взаимодействия небольших объектов посредством сообщений, поэтому соображения, которые мы учитываем для монолитных архитектур, верны и здесь. Тем не менее, поскольку наследование и композиция реализуют два разных типа делегирования (быть и иметь), они оба ценны, и множественное наследование — это способ снять ограничение единственного поставщика, которое возникает из-за наличия только одного родительского класса. Почему это так противоречиво?Учитывая то, что я только что сказал, множественное наследование кажется благословением. Когда объект может наследоваться от нескольких родителей, мы можем легко распределять обязанности между различными классами и использовать только те, которые нам нужны, способствуя повторному использованию кода и избегая «божественных» объектов. К сожалению, все не так просто. Прежде всего, мы сталкиваемся с проблемой, с которой сталкивается каждая микросервисно-ориентированная архитектура, то есть с риском перехода от «божественных» объектов (экстремальная монолитная архитектура) к почти пустым объектам (экстремально распределенный подход), обременяя программиста слишком тонким детальным контролем, который в конечном итоге приводит к созданию системы, в которой отношения между объектами настолько сложны, что становится невозможным понять влияние изменений в коде. Однако в множественном наследовании существует более насущная проблема. Как это происходит с естественным наследованием, родители могут предоставить одну и ту же «генетическую черту» в двух разных вариантах, но у получающегося в результате этого человека будет только одна. Оставляя в стороне генетику (которая невероятно сложнее, чем программирование) и возвращаясь к ООП, мы сталкиваемся с проблемой, когда объект наследуется от двух других объектов, которые предоставляют тот же атрибут. Итак, если ваш класс Child наследует от родителей Parent1 и Parent2, и оба предоставляют метод __init__, какой из них следует использовать вашему объекту? Ситуация может ухудшиться, так как родители могут иметь разные входные параметры общего метода, например Проблема может быть расширена еще дальше, введя общего предка выше Parent1 и Parent2. Как видите, у нас уже есть проблема, когда мы представляем нескольких родителей, а общий предок просто добавляет новый уровень сложности. Класс предка может быть явно в любой точке дерева наследования (дедушка, бабушка и дедушка и т. д.), Важной частью является то, что он является общим для Parent1 и Parent2. Это так называемая проблема алмаза, так как граф наследования имеет форму ромба Таким образом, в то время как при наследовании с одним родителем правила просты, с множественным наследованием у нас сразу возникает более сложная ситуация, в которой нет тривиального решения. Помогает ли все это реализовать множественное наследование? Как мы вскоре увидим, есть решения этой проблемы, но этот дополнительный уровень сложности делает множественное наследование чем-то, что не очень легко вписывается в дизайн. Помните, что наследование — это механизм автоматического делегирования. По этим причинам множественное наследование часто изображается как пугающее и запутанное, и, как правило, ему уделяется время только в продвинутых курсах ООП, по крайней мере, в мире Python. Я считаю, что каждый программист на Python должен ознакомиться с ним и узнать, как им воспользоваться. Множественное наследование: путь PythonПосмотрим, как можно решить алмазную проблему. В отличие от генетики, мы, программисты, не можем позволить себе какой-либо уровень неопределенности или случайности в наших процессах, поэтому при наличии возможной неоднозначности, как та, которая создается множественным наследованием, нам необходимо записать правило, которое будет строго соблюдаться в каждом случае. В Python это правило называется MRO (Method Resolution Order — Порядок разрешения методов), которое было введено в Python 2.3 и описано в этом документе Микеле Симионато. Можно многое сказать о MRO и лежащем в основе алгоритме линеаризации C3, но для целей этого поста достаточно посмотреть, как он решает проблему алмазов. В случае множественного наследования Python следует обычным правилам наследования (автоматическое делегирование предку, если атрибут не присутствует локально), но порядок следования по дереву наследования теперь включает все классы, указанные в сигнатуре класса. В приведенном выше примере Python будет искать атрибуты в следующем порядке: Child, Parent1, Parent2, Ancestor. Таким образом, как и в случае стандартного наследования, это означает, что первый класс в списке, который реализует определенный атрибут, будет выбранным поставщиком для этого разрешения. Пример может прояснить вопрос В этом случае экземпляр c Child обеспечит rewind, open, close и flush. Когда вызывается c.rewind, выполняется код в Ancestor, так как это первый класс в списке MRO, который предоставляет этот метод. Метод open предоставляется Parent1, а close — Parent2. Если вызывается метод c.flush, код предоставляется самим классом Child, который переопределяет его, переопределяя класс, предоставленный Parent2. Как мы видим в методе flush, Python не меняет своего поведения, когда речь идет о методе, переопределенной несколькими родителями. Первая реализация метода с таким именем выполняется, и реализация родителя не вызывается автоматически. Как и в случае стандартного наследования, мы должны определять классы с соответствующими сигнатурами методов. Под капотомКак множественное наследование работает внутри? Как Python создает список MRO? У Python очень простой подход к ООП (более подробнее см. Здесь). Классы сами являются объектами, поэтому они содержат структуры данных, которые используются языком для предоставления функций, и делегирование не исключение. Когда мы запускаем метод для объекта, Python по умолчанию используется метод __getattribute__ (предоставляемый object), который использует __class__ для доступа к классу из экземпляра и __bases__ для поиска родительских классов. Последний, в частности, является кортежем, поэтому он упорядочен и содержит все классы, от которых наследуется текущий класс. MRO создается с использованием только __bases__, но основной алгоритм не так уж простой и связан с монотонностью линеаризации результирующего класса. Но на самом деле это не так страшно, как кажется, но, скорее всего не то, что вы хотели бы прочитать во время отдыха. Но если это так, то вышеупомянутый документ Микеле Симионато содержит все подробности о линеаризации классов, которые вы всегда хотели исследовать, к примеру лежа на пляже. Наследование и интерфейсыЧтобы приблизиться к миксинам, нам нужно подробно обсудить наследование и, в частности, роль сигнатур методов. В Python, когда вы переопределяете метод, предоставленный классом-предком, вы должны решить, когда и как вызывать его начальную реализацию. Это дает программисту свободу решать, нужно ли им просто дополнить метод или полностью заменить его. Помните, что единственное, что делает Python, когда класс наследует от другого, — это автоматически делегирует методы, которые не были реализованы в наследнике. Когда класс наследует от другого, мы в идеале создаем объекты, которые сохраняют обратную совместимость с интерфейсом родительского класса, чтобы позволить их полиморфное использование. Это означает, что когда мы наследуем класс и переопределяем метод, изменяя его сигнатуру, мы делаем что-то опасное и, по крайней мере, с точки зрения полиморфизма. Посмотрите на этот пример Обратите внимание, что Square меняет подпись __init__ и resize. Теперь, когда мы создаем экземпляры этих классов, нам нужно помнить что в Square другая подпись __init__ Обычно мы признаем, что расширенная версия класса принимает больше параметров при инициализации, так как мы не ожидаем, что она будет полиморфной в __init__. Проблемы возникают, когда мы пытаемся использовать полиморфизм в других методах, например, изменяя размер всех объектов GraphicalEntity в списке Поскольку r1, r2 и q1 — все объекты, которые наследуются от GraphicalEntity, мы ожидаем, что они предоставят интерфейс, предоставленный этим классом, но это не удастся, потому что Square изменил сигнатуру resize. То же самое произойдет, если мы создадим их в цикле for из списка классов, но, как я уже сказал, общепринято, что дочерние классы изменяют сигнатуру метода __init__. Это не так, например, в системе на основе плагинов, где все плагины должны быть инициализированы одинаково. Это классическая проблема в ООП. Хотя мы, как люди, воспринимаем квадрат просто как немного особенный прямоугольник, с точки зрения интерфейса эти два класса различны, и поэтому мы не должны находиться в одном и том же дереве наследования, когда имеем дело с измерениями. Это важное соображение: Rectangle и Square полиморфны в методе move, но не в __init__ и resize. Итак, вопрос в том, можем ли мы как-то разделить две природы movable и resizable. Теперь для обсуждения интерфейсов, полиморфизма и причин, лежащих в их основе, потребуется совершенно отдельный пост, поэтому в следующих разделах я собираюсь игнорировать этот вопрос и просто считать интерфейс объекта необязательным. Таким образом, вы найдете примеры объектов, которые нарушают интерфейс родителя, и объектов, которые его хранят. Просто помните: всякий раз, когда вы изменяете сигнатуру метода, вы изменяете (неявный) интерфейс объекта, и таким образом вы останавливаете полиморфизм. Я буду обсуждать в другой раз, если я считаю это правильным или неправильным. МиксиныMRO — это хорошее решение, которое предотвращает двусмысленность, но оставляет программистам ответственность за создание разумных деревьев наследования. Алгоритм помогает разрешать сложные ситуации, но это не значит, что мы должны их создавать. Итак, как мы можем использовать множественное наследование, не создавая системы, которые слишком сложны для понимания? Кроме того, возможно ли использовать множественное наследование для решения проблемы управления двойной (или множественной) природой объекта, как в предыдущем примере с movable и resizable формой? Решение исходит из смешанных классов: это небольшие классы, которые предоставляют атрибуты, но не включены в стандартное дерево наследования, работая скорее как «дополнения» к текущему классу, чем как настоящие предки. Миксины происходят от языка программирования LISP, а именно от того, что можно считать первой версией Common Lisp Object System, расширением Flavors. Современные языки ООП реализуют миксины разными способами: например, в Scala есть функции, называемые traits, которые живут в своем собственном пространстве с определенной иерархией, и которые не мешают правильному наследованию классов. Классы миксинов в PythonPython не поддерживает миксины с какой-либо выделенной функцией языка, поэтому мы используем множественное наследование для их реализации. Это явно требует большой дисциплины от программиста, поскольку это нарушает одно из основных допущений для миксинов: их ортогональность к дереву наследования. В Python так называемые миксины — это классы, которые живут в обычном дереве наследования, но они остаются небольшими, чтобы избежать создания иерархий, которые слишком сложны для понимания программистом. В частности, миксины не должны иметь общих предков, кроме объекта, с другими родительскими классами. Давайте посмотрим на простой пример Здесь класс ResizableMixin наследуется не от GraphicalEntity, а непосредственно от object, поэтому ResizableGraphicalEntity получает от него только метод resize. Как мы уже говорили, это упрощает дерево наследования ResizableGraphicalEntity и помогает снизить риск проблемы алмаза. Это позволяет нам свободно использовать GraphicalEntity в качестве родителя для других классов без необходимости наследовать методы, которые нам не нужны. Пожалуйста, помните, что это происходит потому, что классы предназначены для того, чтобы этого избежать, а не из-за особенностей языка: алгоритм MRO просто гарантирует, что всегда будет однозначный выбор в случае нескольких предков. Mixins обычно не могут быть слишком общими. В конце концов, они предназначены для добавления определенных функций в классы, но эти новые функции часто взаимодействуют с другими уже существующими функциями расширенного класса. В этом случае метод resize взаимодействует с атрибутами size_x и size_y, которые должны присутствовать в объекте. Очевидно, что есть примеры чистых mixins, но, поскольку они не требуют инициализации, их область действия определенно ограничена. Использование миксинов для взламывания наследованияБлагодаря MRO программисты Python могут использовать множественное наследование для переопределения методов, которые объекты наследуют от своих родителей, что позволяет им создавать классы без дублирования кода. Давайте посмотрим на этот пример Как вы можете видеть, класс Button расширяет класс GraphicalEntity классическим способом, используя super для вызова родительского метода __init__ перед добавлением нового атрибута состояния status. Теперь, если я хочу создать класс SquareButton, у меня есть два варианта. Я мог бы просто переопределить __init__ в новом классе который выполняет запрошенное задание, но тесно связывает особенность наличия одного измерения с Button. Если бы мы хотели создать круглое изображение, мы не могли бы наследовать от SquareButton, так как изображение имеет другую природу. Второй вариант — это выделение объектов, связанных с наличием одного измерения в классе mixin, и добавление его в качестве родительского для нового класса. Второе решение дает тот же конечный результат, но способствует повторному использованию кода, поскольку теперь класс SingleDimensionMixin можно применять к другим классам, производным от GraphicalEntity, и заставлять их принимать только один размер, тогда как в первом решении эта функция была тесно связана с предком Button класс. Обратите внимание, что позиция миксина важна. Поскольку super следует MRO, вызываемый метод отправляется в ближайший класс линеаризации. Если вы поместите SingleDimensionMixin после Button в определении SquareButton, Python выдаст ошибку. В этом случае вызов b = SquareButton (10, 20, 200) и сигнатура метода __init__ (self, pos_x, pos_y, size_x, size_y) не будут совпадать. Тем не менее, миксины используются не только тогда, когда вы хотите изменить интерфейс объекта. Используя super, мы можем достичь интересных конструкций, таких как Здесь LimitSizeButton вызывает __init__ своего первого родителя, который является Button. Это, однако, делегирует вызов следующему классу в MRO перед инициализацией self.status, поэтому вызов отправляется LimitSizeMixin, который сначала выполняет некоторые изменения и в конечном итоге отправляет его исходному получателю, GraphicalEntity. Помните, что в Python вы никогда не будете вынуждены вызывать родительскую реализацию метода, поэтому миксин здесь также может остановить механизм диспетчеризации, если это является требованием бизнес-логики нового объекта. Реальный пример: представления на основе классов DjangoНаконец, давайте перейдем к первоначальному источнику вдохновения для этого поста: кодовой базе Django. Я покажу вам, как программисты Django использовали множественные классы наследования и миксины для содействия повторному использованию кода, и теперь вы, надеюсь, поймете все причины, стоящие за ними. Выбранный мной пример можно найти в коде универсальных представлений(generic views), в частности в двух классах: TemplateResponseMixin и TemplateView. Как вы, возможно, знаете, класс Django View является прародителем всех представлений на основе классов и предоставляет метод диспетчеризации dispatch, который преобразует методы HTTP-запросов в вызовы функций Python (CODE). Теперь TemplateView — это представление, которое отвечает на запрос GET, отображающий шаблон с данными, поступающими из контекста, передаваемого при вызове представления. Учитывая механизм, лежащий в основе представлений Django, TemplateView должен реализовать метод get и вернуть содержимое HTTP-ответа. Код класса Как вы можете видеть, TemplateView — это View, но он использует два миксина для добавления функций. Давайте посмотрим на TemplateResponseMixin [Я удалил код класса, так как он не имеет решающего значения для настоящего обсуждения, вы можете увидеть полный класс здесь] Понятно, что TemplateResponseMixin просто добавляет к любому классу два метода get_template_names и render_to_response. Последний вызывается в методе get TemplateView для создания ответа. Давайте посмотрим на упрощенную схему вызовов: Как мы уже говорили, миксины не могут быть слишком общими, и здесь мы видим хороший пример миксина, предназначенного для работы с конкретными классами. TemplateResponseMixin должен применяться к классам, которые содержат self.request (CODE), и хотя это не означает исключительно классы, производные от View, ясно, что он был разработан для дополнения этого конкретного типа. ВыводыЗаключениеЯ надеюсь, что этот пост помог вам понять немного больше, как работает множественное наследование, и меньше быть напуганным этим. Я также надеюсь, что мне удалось показать вам, что классы должны быть тщательно спроектированы, и что при создании системы классов нужно учитывать многое. Еще раз, пожалуйста, помните что композиция, это мощный и часто забываемый инструмент. Обратная связьНе стесняйтесь связаться со мной в Twitter, если у вас есть вопросы. Страница вопросов GitHub — лучшее место для отправки исправлений.
|
---|