смешивание
В объектно-ориентированных языках программирования ( примесь или примесь ) [1] [2] [3] [4] — это класс , который содержит методы для использования другими классами, но не обязательно должен быть родительским классом этих других классов. То, как эти другие классы получают доступ к методам примеси, зависит от языка. Миксины иногда называют «включенными», а не «унаследованными».
Миксины поощряют повторное использование кода и могут использоваться, чтобы избежать неоднозначности наследования, которую может вызвать множественное наследование. [5] (« проблема ромба ») или обойти отсутствие поддержки множественного наследования в языке. Миксин также можно рассматривать как интерфейс с реализованными методами . Этот шаблон является примером реализации принципа инверсии зависимостей .
История
[ редактировать ]Миксины впервые появились в компании Symbolics объектно-ориентированной системе Flavors (разработанной Говардом Кэнноном), которая представляла собой подход к объектной ориентации, используемый в Lisp Machine Lisp . Название было вдохновлено кафе-мороженым Steve's в Сомервилле, штат Массачусетс: [1] Владелец магазина мороженого предложил базовый вкус мороженого (ванильное, шоколадное и т. д.) и смешал его с дополнительными ингредиентами (орехи, печенье, помадка и т. д.) и назвал этот продукт « примесью ». , его собственный термин, зарегистрированный в то время. [2]
Определение
[ редактировать ]Миксины — это языковая концепция, которая позволяет программисту вставлять некоторый код в класс . Миксин-программирование — это стиль разработки программного обеспечения , при котором функциональные единицы создаются в классе, а затем смешиваются с другими классами. [6]
Класс миксина действует как родительский класс, содержащий желаемую функциональность. Подкласс может затем наследовать или просто повторно использовать эту функциональность, но не в качестве средства специализации. Обычно примесь экспортирует желаемую функциональность в дочерний класс , не создавая жестких единых отношений «является». В этом заключается важное различие между концепциями миксинов и наследования , заключающееся в том, что дочерний класс по-прежнему может наследовать все функции родительского класса, но не обязательно применять семантику о том, что дочерний элемент «является своего рода» родителем.
Преимущества
[ редактировать ]- Он обеспечивает механизм множественного наследования , позволяя одному классу использовать общие функции нескольких классов, но без сложной семантики множественного наследования. [7]
- Возможность повторного использования кода . Миксины полезны, когда программист хочет разделить функциональность между разными классами. Вместо повторения одного и того же кода снова и снова, общие функции можно просто сгруппировать в примесь, а затем включить в каждый класс, который в ней нуждается. [8]
- Миксины позволяют наследовать и использовать только нужные функции родительского класса, а не все функции родительского класса. [9]
Реализации
[ редактировать ]В Simula классы определяются в блоке, в котором атрибуты, методы и инициализация класса определяются вместе; таким образом, все методы, которые можно вызвать в классе, определены вместе, и определение класса завершено.
Во Flavors миксин — это класс, от которого другой класс может наследовать определения слотов и методы. Миксин обычно не имеет прямых экземпляров. Поскольку аромат может наследовать более чем от одного другого аромата, он может наследовать от одного или нескольких примесей. Обратите внимание, что оригинальные Flavors не использовали универсальные функции.
В New Flavors (преемнике Flavors) и CLOS методы организованы в « универсальные функции ». Эти универсальные функции представляют собой функции, которые определяются в нескольких случаях (методах) с помощью комбинаций диспетчеризации классов и методов.
CLOS и Flavors позволяют методам примеси добавлять поведение к существующим методам: :before
и :after
демоны, громадины и обертки во Flavors. КЛОС добавлен :around
методы и возможность вызывать скрытые методы через CALL-NEXT-METHOD
. Так, например, миксин-блокировка потока может добавить блокировку существующих методов класса потока. В Flavors можно было бы написать обертку или громадину, а в CLOS можно было бы использовать :around
метод. И CLOS, и Flavors допускают повторное использование с помощью комбинаций методов. :before
, :after
и :around
методы являются особенностью стандартной комбинации методов. Предусмотрены другие комбинации методов.
Примером является +
комбинация методов, в которой результирующие значения каждого из применимых методов универсальной функции арифметически складываются для вычисления возвращаемого значения. Это используется, например, с примесью border-mixin для графических объектов. Графический объект может иметь общую функцию ширины. Border-mixin добавляет рамку вокруг объекта и имеет метод вычисления ее ширины. Новый класс bordered-button
(это одновременно графический объект и использует border
mixin) будет вычислять свою ширину, вызывая все применимые методы ширины — через +
комбинация методов. Все возвращаемые значения складываются и создают общую ширину объекта.
В документе OOPSLA 90 [10] Гилад Брача и Уильям Кук по-новому интерпретируют различные механизмы наследования, обнаруженные в Smalltalk, Beta и CLOS, как особые формы наследования миксинов.
Языки программирования, использующие миксины
[ редактировать ]Помимо Flavors и CLOS (часть Common Lisp ), некоторые языки, использующие примеси:
- Ада (путем расширения существующей записи с тегами произвольными операциями в обобщенном виде)
- C# (начиная с C# 8.0, с помощью по умолчанию ) методов интерфейсов [11]
- Кобра
- ColdFusion (на основе классов с использованием включений и на основе объектов путем назначения методов одного объекта другому во время выполнения)
- Curl (с Curl RTE)
- D (называемый «миксином шаблона» ; D также включает в себя оператор «миксина» , который компилирует строки в виде кода.)
- Дарт
- Фактор [12]
- классный
- Java (начиная с Java 8, с помощью по умолчанию ) методов интерфейсов
- Делегирование JavaScript — функции как роли (трейты и миксины)
- Котлин
- Меньше
- Магия
- МАТЛАБ [13]
- OCaml [14]
- Perl (через роли в расширении Moose объектной системы Perl 5)
- PHP « Особенности »
- Питон
- Racket ( документация по миксинам )
- Раку
- Руби
- Ржавчина
- Sass (язык таблиц стилей)
- Скала [15]
- Смолток
- Быстрый
- СистемаVerilog
- XOTcl / TclOO (объектные системы, встроенные в Tcl ) [16]
- TypeScript ( документация по миксинам )
- Налить
Некоторые языки не поддерживают примеси на уровне языка, но могут легко имитировать их, копируя методы из одного объекта в другой во время выполнения, тем самым «заимствуя» методы примеси. Это также возможно со статически типизированными языками, но для этого требуется создание нового объекта с расширенным набором методов.
Другие языки, не поддерживающие примеси, могут поддерживать их окольным путем через другие языковые конструкции. Например, Visual Basic .NET и C# поддерживают добавление методов расширения к интерфейсам. Это означает, что любой класс, реализующий интерфейс с определенными методами расширения, будет иметь методы расширения, доступные как псевдочлены.
Примеры
[ редактировать ]В Коммон Лиспе
[ редактировать ]Common Lisp предоставляет примеси в CLOS (Common Lisp Object System), аналогичные Flavors.
object-width
— это универсальная функция с одним аргументом, использующая +
комбинация методов. Эта комбинация определяет, что будут вызваны все применимые методы для универсальной функции и будут добавлены результаты.
(defgeneric object-width (object)
(:method-combination +))
button
— это класс с одним слотом для текста кнопки.
(defclass button ()
((text :initform "click me")))
Для объектов класса button существует метод, который вычисляет ширину на основе длины текста кнопки. +
— квалификатор метода для одноименной комбинации методов.
(defmethod object-width + ((object button))
(* 10 (length (slot-value object 'text))))
А 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
.
? (object-width (make-instance 'button))
80
Мы также можем вычислить ширину bordered-button
. Вызов object-width
вычисляет 84. Результатом является сумма результатов двух применимых методов: метода object-width
для класса button
и метод object-width
для класса border-mixin
.
? (object-width (make-instance 'bordered-button))
84
На Python
[ редактировать ]В Python пример концепции миксина можно найти в файле SocketServer
модуль, [17] который имеет оба UDPServer
класс и TCPServer
сорт. Они действуют как серверы для серверов сокетов UDP и TCP соответственно. Кроме того, есть два класса миксинов: ForkingMixIn
и ThreadingMixIn
. Обычно все новые соединения обрабатываются в рамках одного и того же процесса. Расширяя TCPServer
с ThreadingMixIn
следующее:
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
pass
тот ThreadingMixIn
Класс добавляет функциональность TCP-серверу, так что каждое новое соединение создает новый поток . Используя тот же метод, А. ThreadingUDPServer
можно создать без необходимости дублировать код в ThreadingMixIn
. Альтернативно, используя ForkingMixIn
процесса приведет к разветвлению для каждого нового соединения. Очевидно, что возможность создания нового потока или разветвления процесса не очень полезна в качестве отдельного класса.
В этом примере использования примеси предоставляют альтернативную базовую функциональность, не затрагивая функциональность сервера сокетов.
В Рубине
[ редактировать ]Большая часть мира Ruby основана на миксинах через Modules
. Концепция миксинов реализована в Ruby с помощью ключевого слова include
которому мы передаем имя модуля в качестве параметра .
Пример:
class Student
include Comparable # The class Student inherits the Comparable module using the 'include' keyword
attr_accessor :name, :score
def initialize(name, score)
@name = name
@score = score
end
# Including the Comparable module requires the implementing class to define the <=> comparison operator
# Here's the comparison operator. We compare 2 student instances based on their scores.
def <=>(other)
@score <=> other.score
end
# Here's the good bit - I get access to <, <=, >,>= and other methods of the Comparable Interface for free.
end
s1 = Student.new("Peter", 100)
s2 = Student.new("Jason", 90)
s1 > s2 #true
s1 <= s2 #false
В JavaScript
[ редактировать ]Объектно -литеральный и extend
Подход
Технически возможно добавить поведение к объекту, привязав функции к ключам в объекте. Однако отсутствие разделения между состоянием и поведением имеет недостатки:
- Он смешивает свойства предметной области модели со свойствами предметной области реализации.
- Никакого разделения общего поведения. Метаобъекты решают эту проблему, отделяя свойства объектов, специфичные для предметной области, от свойств, специфичных для их поведения. [18]
Функция расширения используется для смешивания поведения: [19]
'use strict';
const Halfling = function (fName, lName) {
this.firstName = fName;
this.lastName = lName;
};
const mixin = {
fullName() {
return this.firstName + ' ' + this.lastName;
},
rename(first, last) {
this.firstName = first;
this.lastName = last;
return this;
}
};
// An extend function
const extend = (obj, mixin) => {
Object.keys(mixin).forEach(key => obj[key] = mixin[key]);
return obj;
};
const sam = new Halfling('Sam', 'Loawry');
const frodo = new Halfling('Freeda', 'Baggs');
// Mixin the other methods
extend(Halfling.prototype, mixin);
console.log(sam.fullName()); // Sam Loawry
console.log(frodo.fullName()); // Freeda Baggs
sam.rename('Samwise', 'Gamgee');
frodo.rename('Frodo', 'Baggins');
console.log(sam.fullName()); // Samwise Gamgee
console.log(frodo.fullName()); // Frodo Baggins
Миксин с использованием Object.assign()
'use strict';
// Creating an object
const obj1 = {
name: 'Marcus Aurelius',
city: 'Rome',
born: '121-04-26'
};
// Mixin 1
const mix1 = {
toString() {
return `${this.name} was born in ${this.city} in ${this.born}`;
},
age() {
const year = new Date().getFullYear();
const born = new Date(this.born).getFullYear();
return year - born;
}
};
// Mixin 2
const mix2 = {
toString() {
return `${this.name} - ${this.city} - ${this.born}`;
}
};
// Adding the methods from mixins to the object using Object.assign()
Object.assign(obj1, mix1, mix2);
console.log(obj1.toString()); // Marcus Aurelius - Rome - 121-04-26
console.log(`His age is ${obj1.age()} as of today`); // His age is 1897 as of today
на основе чистых функций и делегирования Подход Flight-Mixin
Несмотря на то, что первый описанный подход наиболее широко распространен, следующий ближе к тому, что принципиально предлагает ядро языка JavaScript — Делегирование .
Два шаблона на основе функциональных объектов уже делают свое дело без необходимости реализации третьей стороной. extend
.
'use strict';
// Implementation
const EnumerableFirstLast = (function () { // function based module pattern.
const first = function () {
return this[0];
},
last = function () {
return this[this.length - 1];
};
return function () { // function based Flight-Mixin mechanics ...
this.first = first; // ... referring to ...
this.last = last; // ... shared code.
};
}());
// Application - explicit delegation:
// applying [first] and [last] enumerable behavior onto [Array]'s [prototype].
EnumerableFirstLast.call(Array.prototype);
// Now you can do:
const a = [1, 2, 3];
a.first(); // 1
a.last(); // 3
На других языках
[ редактировать ]В языке веб-контента Curl используется множественное наследование, поскольку классы без экземпляров могут реализовывать методы. Общие миксины включают в себя все изменяемые скины. ControlUI
наследуется от SkinnableControlUI
, объекты-делегаты пользовательского интерфейса, которым требуются раскрывающиеся меню, унаследованные от StandardBaseDropdownUI, и такие явно названные классы примесей, как FontGraphicMixin
, FontVisualMixin
и NumericAxisMixin-of
сорт. В версии 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] поддерживает Trait и миксин [25] [26] композиция объектов на основе функциональных объектов, которые реализуют дополнительное поведение, а затем делегируются через call
или apply
к объектам, которые нуждаются в такой новой функциональности.
В масштабе
[ редактировать ]Scala имеет богатую систему типов, и Traits являются ее частью, которая помогает реализовать поведение примесей. Как следует из названия, Черты обычно используются для обозначения отдельной особенности или аспекта, который обычно ортогонален ответственности конкретного типа или, по крайней мере, определенного экземпляра. [27] Например, умение петь моделируется как такой ортогональный признак: его можно применить к Птицам, Человекам и т. д.
trait Singer{
def sing { println(" singing … ") }
//more methods
}
class Bird extends Singer
Здесь Bird смешал все методы признака в свое собственное определение, как если бы класс Bird определил метод Sing() самостоятельно.
Как extends
также используется для наследования от суперкласса в случае признака extends
используется, если суперкласс не унаследован и только для примеси в первом признаке. Все следующие характеристики смешиваются с помощью ключевого слова with
.
class Person
class Actor extends Person with Singer
class Actor extends Singer with Performer
Scala позволяет смешивать черты (создавать анонимный тип ) при создании нового экземпляра класса. В случае экземпляра класса Person не все экземпляры могут петь. Эта функция используется тогда:
class Person{
def tell { println (" Human ") }
//more methods
}
val singingPerson = new Person with Singer
singingPerson.sing
В ржавчине
[ редактировать ]Rust широко использует миксины через трейты . Трейты, как и в Scala, позволяют пользователям реализовывать поведение для определенного типа. Они также используются для обобщений и динамической диспетчеризации , позволяя типам, реализующим признак, взаимозаменяемо использоваться статически или динамически во время выполнения. [28]
// Allows for types to "speak"
trait Speak {
fn speak();
// Rust allows implementors to define default implementations for functions defined in traits
fn greet() {
println!("Hi!")
}
}
struct Dog;
impl Speak for Dog {
fn speak() {
println!("Woof woof");
}
}
struct Robot;
impl Speak for Robot {
fn speak() {
println!("Beep beep boop boop");
}
// Here we override the definition of Speak::greet for Robot
fn greet() {
println!("Robot says howdy!")
}
}
В Свифте
[ редактировать ]Миксин можно реализовать в Swift, используя языковую функцию, называемую реализацией по умолчанию в расширении протокола.
protocol ErrorDisplayable {
func error(message:String)
}
extension ErrorDisplayable {
func error(message:String) {
// Do what it needs to show an error
//...
print(message)
}
}
struct NetworkManager : ErrorDisplayable {
func onError() {
error("Please check your internet Connection.")
}
}
См. также
[ редактировать ]- Абстрактный тип
- Шаблон декоратора
- Проектирование на основе политик
- Черта , подобная структура, не требующая линейной композиции.
Ссылки
[ редактировать ]- ^ Jump up to: а б «Использование дополнений с Python | Linux Journal» . www.linuxjournal.com . Проверено 23 мая 2023 г.
- ^ Jump up to: а б AOL.COM, Бапопик (3 августа 2002 г.). «Mix-Ins (мороженое Стива, Бостон, 1975)» . Проверено 23 мая 2023 г.
- ^ Jump up to: а б «Реализация миксинов с помощью методов расширения C#» . Zorched/Однострочное исправление . Проверено 23 мая 2023 г.
- ^ Jump up to: а б «Я знаю ответ (это 42): миксины и C#» . 04 сентября 2006 г. Архивировано из оригинала 4 сентября 2006 г. Проверено 23 мая 2023 г.
- ^ Бойленд, Джон; Джузеппе Кастанья (26 июня 1996 г.). «Типобезопасная компиляция ковариантной специализации: практический пример» . В Пьере Коэнте (ред.). ECOOP '96, Объектно-ориентированное программирование: 10-я Европейская конференция . Спрингер. стр. 16–17. ISBN 9783540614395 . Проверено 17 января 2014 г.
- ^ «Смешать» . wiki.c2.com . Проверено 23 мая 2023 г.
- ^ «Работа с миксинами в Ruby» . 8 июля 2015 г.
- ^ «Повторное использование в ООП: наследование, композиция и миксины» .
- ^ «Выходя за рамки миксинов» Джастин Лейтгеб» . Архивировано из оригинала 25 сентября 2015 г. Проверено 16 сентября 2015 г.
- ^ «Наследование на основе миксинов» (PDF) .
- ^ Билл Вагнер. «Создавайте типы миксинов, используя методы интерфейса по умолчанию» . docs.microsoft.com . Проверено 18 апреля 2022 г.
- ^ слава (25 января 2010 г.). «Фактор/Особенности/Язык» . concatenative.org . Проверено 15 мая 2012 г.
Основные особенности языка Factor:… Объектная система с наследованием, универсальными функциями, диспетчеризацией предикатов и миксинами.
- ^ «Классы — MATLAB и Simulink — MathWorks India» .
- ^ Ален Фриш (14 июня 2013 г.). «Миксины объекты» . ЛексиФи . Проверено 29 марта 2022 г.
- ^ «Композиция классов миксинов» . Федеральная политехническая школа Лозанны . Проверено 16 мая 2014 г.
- ^ «XOTcl — Учебное пособие» . media.wu-wien.ac.at . Проверено 23 мая 2023 г.
- ^ «cpython: 2cb530243943 Lib/socketserver.py» . hg.python.org . Проверено 23 мая 2023 г.
- ^ «Миксины, пересылка и делегирование в JavaScript» .
- ^ «СУХОЙ JavaScript с миксинами» . Архивировано из оригинала 21 сентября 2015 г. Проверено 16 сентября 2015 г.
- ^ «Методы по умолчанию (Учебные пособия по Java™ > Изучение языка Java > Интерфейсы и наследование)» .
- ^ Миксины, дженерики и методы расширения в C#.
- ^ «Методы расширения» . flutterbyexample.com . Проверено 23 мая 2023 г.
- ^ «Создание типов примесей с использованием методов интерфейса по умолчанию | Microsoft Docs» . 13 апреля 2020 г. Архивировано из оригинала 13 апреля 2020 г. Проверено 23 мая 2023 г.
- ^ Селигер, Питер (11 апреля 2014 г.). «Дрехтюр: множество талантов JavaScript» . Дретюр . Проверено 23 мая 2023 г.
- ^ Кролл, Ангус (31 мая 2011 г.). «Свежий взгляд на миксины JavaScript» . JavaScript, JavaScript.. . Проверено 23 мая 2023 г.
- ^ «Javascript-code-reuse-patterns/source/comComponents/composition at master · petsel/javascript-code-reuse-patterns» . Гитхаб . Проверено 23 мая 2023 г.
- ^ «Scala на практике: черты как миксины – мотивация» . 19 июля 2009 г.
- ^ «Черты: определение общего поведения — язык программирования Rust» .
Внешние ссылки
[ редактировать ]- MixIn в репозитории шаблонов Портленда
- Миксины в ActionScript
- Объектная система Common Lisp: обзор Ричарда П. Габриэля и Линды ДеМихил представляет собой хорошее введение в мотивацию определения классов с помощью обобщенных функций.