stringtranslate.com

Миксин

В объектно-ориентированных языках программирования примесь (или примесь ) [1] [2] [3] [4] — это класс , который содержит методы для использования другими классами, но не обязательно должен быть родительским классом для этих других классов. То, как эти другие классы получают доступ к методам примеси, зависит от языка. Миксины иногда называют «включенными», а не «унаследованными».

Миксины поощряют повторное использование кода и могут использоваться, чтобы избежать неоднозначности наследования, которую может вызвать множественное наследование [5]проблема ромба »), или для обхода отсутствия поддержки множественного наследования в языке. Миксин также можно рассматривать как интерфейс с реализованными методами . Этот шаблон является примером реализации принципа инверсии зависимостей .

История

Миксины впервые появились в объектно-ориентированной системе Flavors компании Symbolics (разработанной Говардом Кэнноном), которая представляла собой подход к объектной ориентации, используемый в Lisp Machine Lisp . Название было вдохновлено кафе-мороженым «Стивс» в Сомервилле, штат Массачусетс: [1] Владелец магазина мороженого предлагал базовый вкус мороженого (ванильное, шоколадное и т. д.) и смешивал его с дополнительными ингредиентами (орехи, печенье, помадка и т. д.) и назвал этот продукт « примесью », что в то время было его собственным товарным знаком. [2]

Определение

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

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

Преимущества

  1. Он обеспечивает механизм множественного наследования , позволяя одному классу использовать общие функции нескольких классов, но без сложной семантики множественного наследования. [7]
  2. Возможность повторного использования кода . Миксины полезны, когда программист хочет разделить функциональность между разными классами. Вместо повторения одного и того же кода снова и снова, общие функции можно просто сгруппировать в примесь, а затем включить в каждый класс, который в ней нуждается. [8]
  3. Миксины позволяют наследовать и использовать только нужные функции родительского класса, а не все функции родительского класса. [9]

Реализации

В Simula классы определяются в блоке, в котором атрибуты, методы и инициализация класса определяются вместе; таким образом, все методы, которые можно вызвать в классе, определены вместе, и определение класса завершено.

Во Flavors миксин — это класс, от которого другой класс может наследовать определения слотов и методы. Миксин обычно не имеет прямых экземпляров. Поскольку аромат может наследовать более чем от одного другого аромата, он может наследовать от одного или нескольких примесей. Обратите внимание, что оригинальные Flavors не использовали универсальные функции.

В New Flavors (преемнике Flavors) и CLOS методы организованы в « универсальные функции ». Эти общие функции представляют собой функции, которые определяются в нескольких случаях (методах) с помощью комбинаций диспетчеризации классов и методов.

CLOS и Flavors позволяют методам примеси добавлять поведение к существующим методам: :beforeа также :afterдемонам, вопперам и оберткам во Flavors. В CLOS добавлены :aroundметоды и возможность вызывать скрытые методы через CALL-NEXT-METHOD. Так, например, миксин-блокировка потока может добавить блокировку существующих методов класса потока. В Flavors можно было бы написать обертку или громадину, а в CLOS можно было бы использовать :aroundметод. И CLOS, и Flavors допускают повторное использование с помощью комбинаций методов. :beforeи :afterметоды :aroundявляются особенностью стандартной комбинации методов. Предусмотрены другие комбинации методов.

Примером является +комбинация методов, в которой результирующие значения каждого из применимых методов универсальной функции арифметически складываются для вычисления возвращаемого значения. Это используется, например, с примесью border-mixin для графических объектов. Графический объект может иметь общую функцию ширины. Border-mixin добавляет рамку вокруг объекта и имеет метод вычисления ее ширины. Новый класс bordered-button(который одновременно является графическим объектом и использует примесь border) будет вычислять свою ширину, вызывая все применимые методы ширины — через +комбинацию методов. Все возвращаемые значения складываются и создают общую ширину объекта.

В статье OOPSLA 90 [10] Гилад Брача и Уильям Кук по-новому интерпретируют различные механизмы наследования, обнаруженные в Smalltalk, Beta и CLOS, как особые формы наследования миксинов.

Языки программирования, использующие миксины

Помимо Flavors и CLOS (часть Common Lisp ), некоторые языки, использующие примеси:

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

Другие языки, не поддерживающие примеси, могут поддерживать их окольным путем через другие языковые конструкции. Например, Visual Basic .NET и C# поддерживают добавление методов расширения к интерфейсам. Это означает, что любой класс, реализующий интерфейс с определенными методами расширения, будет иметь методы расширения, доступные как псевдочлены.

Примеры

В Коммон Лиспе

Common Lisp предоставляет примеси в CLOS (объектная система Common Lisp), аналогичные Flavors.

object-width— это универсальная функция с одним аргументом, использующая +комбинацию методов. Эта комбинация определяет, что будут вызваны все применимые методы для универсальной функции и будут добавлены результаты.

( defgeneric object-width ( object ) ( :method-combination + ))    

button— это класс с одним слотом для текста кнопки.

( кнопка defclass () (( текст :initform "нажми на меня" )))     

Для объектов класса button существует метод, который вычисляет ширину на основе длины текста кнопки. +— квалификатор метода для одноименной комбинации методов.

( defmethod object-width + (( кнопка объекта )) ( * 10 ( длина ( объект значения слота 'текст ))))          

Класс border-mixin. Именование — это всего лишь соглашение. Здесь нет суперклассов и слотов.

( defclass border-mixin () ())   

Существует метод вычисления ширины границы. Здесь всего 4.

( defmethod object-width + (( object border-mixin )) 4 )     

bordered-button— это класс, унаследованный от обоих border-mixinи button.

( defclass bordered-button ( border-mixin button ) ())    

Теперь мы можем вычислить ширину кнопки. Вызов object-widthвычисляет 80. Результатом является результат единственного применимого метода: метода object-widthкласса button.

? ( ширина объекта ( кнопка make-instance ' )) 80   

Мы также можем вычислить ширину файла bordered-button. Вызов object-widthвычисляет 84. Результатом является сумма результатов двух применимых методов: метода object-widthкласса buttonи метода object-widthкласса border-mixin.

? ( ширина объекта ( make-instance 'bordered-button )) 84   

На Python

В Python пример концепции миксина можно найти в SocketServerмодуле [17] , который имеет как UDPServerкласс, так и TCPServerкласс. Они действуют как серверы для серверов сокетов UDP и TCP соответственно. Кроме того, есть два класса миксинов: ForkingMixInи ThreadingMixIn. Обычно все новые соединения обрабатываются в рамках одного и того же процесса. Расширяя TCPServerследующим образом ThreadingMixIn:

класс  ThreadingTCPServer ( ThreadingMixIn ,  TCPServer ):  пройти

этот ThreadingMixInкласс добавляет функциональность TCP-серверу, так что каждое новое соединение создает новый поток . Используя тот же метод, ThreadingUDPServerможно создать файл без необходимости дублировать код в ThreadingMixIn. Альтернативно, использование ForkingMixInприведет к разветвлению процесса для каждого нового соединения. Очевидно, что возможность создания нового потока или разветвления процесса не очень полезна в качестве отдельного класса.

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

В Рубине

Большая часть мира Ruby основана на миксинах через Modules. Концепция миксинов реализована в Ruby с помощью ключевого слова, includeкоторому мы передаем имя модуля в качестве параметра .

Пример:

class Student включает Comparable # Класс Student наследует модуль Comparable, используя ключевое слово 'include' attr_accessor :name , :score        def инициализировать ( имя , оценка ) @name = имя @score = конец оценки          # Включение модуля Comparable требует, чтобы реализующий класс определил оператор сравнения <=> # Вот оператор сравнения. Мы сравниваем два примера учащихся на основе их оценок.  def <=> ( другое ) @score <=> другое . конец счета      # Вот что хорошо: я получаю бесплатный доступ к <, <=, >,>= и другим методам Comparable Interface. конецs1 = Студент . новый ( «Питер» , 100 ) s2 = Студент . новый ( «Джейсон» , 90 )      s1 > s2 #true s1 <= s2 #false      

В JavaScript

Объектно -литеральный подход и extendподход

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

  1. Он смешивает свойства предметной области модели со свойствами предметной области реализации.
  2. Никакого разделения общего поведения. Метаобъекты решают эту проблему, отделяя свойства объектов, специфичные для предметной области, от свойств, специфичных для их поведения. [18]

Функция расширения используется для смешивания поведения: [19]

'используйте строгий' ;const Halfling = функция ( fName , lName ) { this . Имя = имя_фамилии ; этот . Фамилия = Имя Имя ; };            const mixin = { fullName () { верните это . имя + '' + это . фамилия ; }, переименуйте ( первый , последний ) { this . ПервоеИмя = первый ; этот . Фамилия = последний ; вернуть это ; } };                        // Функция расширения const Extend = ( obj , mixin ) => { Object . ключи ( миксин ). forEach ( ключ => объект [ ключ ] = миксин [ ключ ]); вернуть объект ; };             const sam = новый полурослик ( «Сэм» , «Лоури» ); const frodo = новый халфлинг ( «Фрида» , «Бэггс» );          // Подмешиваем другие методы расширения ( Halfling.prototype , mixin ) ; консоль . журнал ( Сэм . FullName ()); // Консоль Сэма Лоури . журнал ( frodo.fullName ( )) ; // Фрида Бэггс  Сэм . переименовать ( 'Samwise' , 'Gamgee' ); Фродо . переименовать ( «Фродо» , «Бэггинс» );  консоль . журнал ( Сэм . FullName ()); // Консоль Samwise Gamgee . журнал ( frodo.fullName ( )) ; // Фродо Бэггинс  

Миксин с использованием Object.assign()

'используйте строгий' ;// Создание объекта const obj1 = { name : 'Марк Аврелий' , город : 'Рим' , родился : '121-04-26' };         // Mixin 1 const mix1 = { toString () { return ` $ { this . name } родился в ${ this . город } в ${ this . родился } ` ; }, возраст () { const год = новая дата (). получитьПолныйГод (); constborn = новая дата ( this.born ) . _ _ получитьПолныйГод (); возврат года рождения ; _ } }; // Mixin 2 const mix2 = { toString () { return ` ${ this . имя } - ${ это . город } - ${ это . родился } ` ; } };                                 // Добавляем методы из миксинов в объект с помощью Object.assign() Object . назначить ( obj1 , mix1 , mix2 );  консоль . журнал ( obj1 . toString ()); // Марк Аврелий — Рим — консоль 121-04-26 . log ( `Его возраст на сегодняшний день ${ obj1.age () }` ) ; // На сегодняшний день ему 1897 год.  

Подход Flight-Mixin на основе чистых функций и делегирования

Несмотря на то, что первый описанный подход наиболее широко распространен, следующий ближе к тому, что принципиально предлагает ядро ​​языка JavaScript — Делегирование .

Два шаблона на основе функциональных объектов уже справляются со своей задачей без необходимости использования сторонней реализации extend.

'используйте строгий' ;// Реализация const EnumerableFirstLast = ( function () { // шаблон модуля на основе функции. const first = function () { return this [ 0 ]; }, Last = function () { return this [ this . length - 1 ]; } ; return function () { // Механика Flight-Mixin на основе функций ... this.first = first ; // ... ссылка на... this.last = Last ; // ... общий код. } ; } ());                                       // Приложение — явное делегирование: // применение [first] и [last] перечислимого поведения к [prototype] массива. EnumerableFirstLast . вызов ( Array.prototype ) ; _// Теперь вы можете сделать: const a = [ 1 , 2 , 3 ]; а . первый (); // 1 а . последний (); // 3       

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

В языке веб-контента Curl используется множественное наследование, поскольку классы без экземпляров могут реализовывать методы. Общие примеси включают в себя все скины, ControlUIунаследованные от SkinnableControlUI, объекты делегата пользовательского интерфейса, для которых требуются раскрывающиеся меню, наследующие от StandardBaseDropdownUI, и такие явно названные классы примесей FontGraphicMixin, как FontVisualMixinи NumericAxisMixin-ofclass. В версии 7.0 добавлен доступ к библиотеке, поэтому примеси не обязательно должны находиться в одном пакете или быть общедоступными абстрактными. Конструкторы Curl — это фабрики, которые позволяют использовать множественное наследование без явного объявления интерфейсов или примесей. [ нужна цитата ]

Интерфейсы и особенности

В Java 8 представлена ​​новая функция в виде методов по умолчанию для интерфейсов. [20] По сути, это позволяет определить метод в интерфейсе с приложением в сценарии, когда новый метод должен быть добавлен в интерфейс после завершения настройки программирования класса интерфейса. Добавление новой функции в интерфейс означает реализацию метода в каждом классе, использующем интерфейс. В этом случае помогают методы по умолчанию, поскольку они могут быть введены в интерфейс в любое время и имеют реализованную структуру, которая затем используется связанными классами. Следовательно, методы по умолчанию добавляют возможность применения концепции примеси в Java.

Интерфейсы в сочетании с аспектно-ориентированным программированием также позволяют создавать полноценные примеси на языках, поддерживающих такие функции, например C# или Java. Кроме того, благодаря использованию шаблона интерфейса маркера , универсального программирования и методов расширения C# 3.0 имеет возможность имитировать примеси. В Dart 2.7 и C# 3.0 появились методы расширения, которые можно применять не только к классам, но и к интерфейсам. Методы расширения предоставляют дополнительную функциональность существующему классу без его изменения. Тогда становится возможным создать статический вспомогательный класс для конкретной функциональности, определяющей методы расширения. Поскольку классы реализуют интерфейс (даже если фактический интерфейс не содержит никаких методов или свойств для реализации), он также будет использовать все методы расширения. [3] [4] [21] В C# 8.0 добавлена ​​функция методов интерфейса по умолчанию. [22] [23]

ECMAScript (в большинстве случаев реализованный как JavaScript) не требует имитации композиции объекта путем пошагового копирования полей из одного объекта в другой. Он изначально [24] поддерживает композицию объектов на основе признаков и миксинов [25] [26] через функциональные объекты, которые реализуют дополнительное поведение, а затем делегируются через callили applyк объектам, которые нуждаются в такой новой функциональности.

В Скала

Scala имеет богатую систему типов, и Traits являются ее частью, которая помогает реализовать поведение примесей. Как следует из названия, Черты обычно используются для обозначения отдельной особенности или аспекта, который обычно ортогонален ответственности конкретного типа или, по крайней мере, определенного экземпляра. [27] Например, умение петь моделируется как такой ортогональный признак: его можно применить к Птицам, Человекам и т. д.

типаж Singer { def Sing { println ( " пение ... " ) } // дополнительные методы }       класс Bird расширяет Singer   

Здесь Bird смешал все методы признака в свое собственное определение, как если бы класс Bird определил метод Sing() самостоятельно.

Как extendsтакже используется для наследования от суперкласса, в случае признака extendsиспользуется, если суперкласс не унаследован, и только для примеси в первом признаке. Все следующие признаки смешиваются с использованием ключевого слова with.

класс Person класс Actor расширяет Person с помощью Singer класс Actor расширяет Singer с помощью Performer           

Scala позволяет смешивать черты (создавать анонимный тип ) при создании нового экземпляра класса. В случае экземпляра класса Person не все экземпляры могут петь. Эта функция используется тогда:

class Person { def Tell { println ( "Human" ) } //больше методов }        val SingingPerson = новый человек с Singer SingingPerson . петь      

В ржавчине

Rust широко использует миксины через трейты . Трейты, как и в Scala, позволяют пользователям реализовывать поведение для определенного типа. Они также используются для дженериков и динамической диспетчеризации , которые позволяют взаимозаменяемо использовать типы с одинаковыми характеристиками статически или динамически во время выполнения соответственно. [28]

// Позволяет типам "говорить" типаж Speak { fn talk ();   // Rust позволяет разработчикам определять реализации по умолчанию для функций, определенных в типажах fn Greeting () { println! ( "Привет!" ) } }  структура  Собака ;impl Speak for Dog { fn talk () { println! ( "Гав гав" ); } }      построить  робота ;impl Speak for Robot { fn talk () { println! ( «Бип-бип-буп-буп» ); }      // Здесь мы переопределяем определение Speak::greet для робота fn Greeting () { println! ( « Робот говорит привет!» ) } }  

В Свифте

Миксин можно реализовать в Swift, используя языковую функцию, называемую реализацией по умолчанию в расширении протокола.

протокол  ErrorDisplayable  {  ошибка функции ( сообщение : строка )}расширение  ErrorDisplayable  {  ошибка функции ( сообщение : строка )  { // Делаем то, что нужно, чтобы показать ошибку //... распечатать ( сообщение ) }}структура  NetworkManager  :  ErrorDisplayable  { функция  onError ()  { ошибка ( «Пожалуйста, проверьте подключение к Интернету». ) }}

Смотрите также

Рекомендации

  1. ^ ab «Использование дополнений с Python | Linux Journal». www.linuxjournal.com . Проверено 23 мая 2023 г.
  2. ^ ab AOL.COM, Bapopik (3 августа 2002 г.). «Mix-Ins (мороженое Стива, Бостон, 1975)» . Проверено 23 мая 2023 г.
  3. ^ ab «Реализация миксинов с помощью методов расширения C#». Zorched/Однострочное исправление . Проверено 23 мая 2023 г.
  4. ^ ab «Я знаю ответ (это 42): миксины и C#». 04 сентября 2006 г. Архивировано из оригинала 4 сентября 2006 г. Проверено 23 мая 2023 г.
  5. ^ Бойленд, Джон; Джузеппе Кастанья (26 июня 1996 г.). «Типобезопасная компиляция ковариантной специализации: практический пример». В Пьере Коэнте (ред.). ECOOP '96, Объектно-ориентированное программирование: 10-я Европейская конференция . Спрингер. стр. 16–17. ISBN 9783540614395. Проверено 17 января 2014 г.
  6. ^ "Смешать". wiki.c2.com . Проверено 23 мая 2023 г.
  7. ^ «Работа с миксинами в Ruby». 8 июля 2015 г.
  8. ^ «Повторное использование в ООП: наследование, композиция и миксины».
  9. ^ «Выходя за рамки миксинов » Джастин Лейтгеб» . Архивировано из оригинала 25 сентября 2015 г. Проверено 16 сентября 2015 г.
  10. ^ «Наследование на основе миксинов» (PDF) .
  11. ^ Билл Вагнер. «Создавайте типы миксинов, используя методы интерфейса по умолчанию». docs.microsoft.com . Проверено 18 апреля 2022 г.
  12. ^ Слава (25 января 2010 г.). «Фактор/Особенности/Язык». concatenative.org . Проверено 15 мая 2012 г. Основные особенности языка Factor:… Объектная система с наследованием, универсальными функциями, диспетчеризацией предикатов и миксинами.
  13. ^ «Классы — MATLAB и Simulink — MathWorks India» .
  14. ^ Ален Фриш (14 июня 2013 г.). «Миксины объекты». ЛексиФи . Проверено 29 марта 2022 г.
  15. ^ «Состав класса миксинов» . Федеральная политехническая школа Лозанны . Проверено 16 мая 2014 г.
  16. ^ "XOTcl - Учебное пособие" . media.wu-wien.ac.at . Проверено 23 мая 2023 г.
  17. ^ "cpython: 2cb530243943 Lib/socketserver.py". hg.python.org . Проверено 23 мая 2023 г.
  18. ^ «Миксины, пересылка и делегирование в JavaScript».
  19. ^ «СУХОЙ JavaScript с миксинами» . Архивировано из оригинала 21 сентября 2015 г. Проверено 16 сентября 2015 г.
  20. ^ «Методы по умолчанию (Учебные пособия по Java™> Изучение языка Java> Интерфейсы и наследование)» .
  21. ^ Миксины, дженерики и методы расширения в C#.
  22. ^ «Методы расширения». flutterbyexample.com . Проверено 23 мая 2023 г.
  23. ^ «Создание типов примесей с использованием методов интерфейса по умолчанию | Microsoft Docs» . 13 апреля 2020 г. Архивировано из оригинала 13 апреля 2020 г. Проверено 23 мая 2023 г.
  24. ^ Селигер, Питер (11 апреля 2014 г.). «Дрехтюр: множество талантов JavaScript». Дретюр . Проверено 23 мая 2023 г.
  25. ^ Кролл, Ангус (31 мая 2011 г.). «Свежий взгляд на миксины JavaScript». JavaScript, JavaScript.. . Проверено 23 мая 2023 г.
  26. ^ "Javascript-code-reuse-patterns/source/comComponents/composition at master · petsel/javascript-code-reuse-patterns" . Гитхаб . Проверено 23 мая 2023 г.
  27. ^ «Scala на практике: черты как миксины - мотивация». 19 июля 2009 г.
  28. ^ «Черты: определение общего поведения - язык программирования Rust» .

Внешние ссылки