Любой разработчик, так или иначе сталкивавшийся с объектно-ориентированным программированием и пытавшийся в нем разобраться, обязательно слышал об ЗАКРЫТЬ , объектная система языка Common Lisp, одной из фундаментальных особенностей которой являются так называемые «обобщенные функции», или, в просторечии, «мультиметоды».
Хотя многие считают, что обобщенные функции — это просто аналог перегрузки статических функций, но только в динамике, это совершенно неверно.
Было бы даже не совсем корректно говорить, что это расширение диспетчеризации self/this, то есть «виртуальных функций», на несколько аргументов.
Конечно, множественная диспетчеризация — одна из основных особенностей обобщенных функций, но сама их суть не только и даже не столько в этом.
Поясню на примере – калькуляторе простых математических выражений.
Если мы возьмем язык с «классической» реализацией ООП, например C#, то в итоге получим что-то вроде этого:
Версия Lisp будет такой:interface IExpression { int Evaluate(); } class Negation : IExpression { IExpression expr; public Negation(IExpression e) { expr = e; } public int Evaluate() { return -expr.Evaluate(); } } class Addition : IExpression { IExpression left, right; public Addition(IExpression l, IExpression r) { left = l; right = r; } public int Evaluate() { return left.Evaluate() + right.Evaluate(); } } class Number : IExpression { int value; public Number(int v) { value = v; } public int Evaluate() { return value; } }
(defstruct (negation
(:constructor negation (expr))
(:conc-name nil))
expr)
(defstruct (addition
(:constructor addition (left right))
(:conc-name nil))
left right)
(defgeneric evaluate (expr)
(:method ((expr negation))
(- (evaluate (expr expr))))
(:method ((expr addition))
(+ (evaluate (left expr))
(evaluate (right expr))))
(:method ((expr number))
expr))
;; (evaluate (addition 5 (negation -5)))
;; ==> 10
Первое, что бросается в глаза, помимо уменьшения размера кода, — это то, что методы отделены от структур данных и сгруппированы в общие функции.
Второе заметное отличие — отсутствие интерфейсов как таковых.
Кроме того, мы не вводили новый класс или структуру данных для представления чисел, а просто специализированы.
оценивать по стандартному классу число .
Вообще отсутствие в CLOS понятий интерфейсов, в ОО-понимании этого слова, и абстрактных классов, довольно сильно, по моим наблюдениям, мешает портированию кода с традиционных объектно-ориентированных языков, а кроме того , создает дополнительные препятствия в изучении языка и объектной модели для людей, привыкших к этим самым традиционным объектно-ориентированным языкам; Кроме того, «требование единообразия сигнатуры универсальной функции» (что означает, что все методы в группе должны иметь одинаковое количество обязательных параметров) создает проблемы для таких людей, поскольку эти люди начинают писать в традиционном объектно-ориентированном стиле, просто предоставляя предполагаемое «это» в качестве первого аргумента.
Возможно, некоторым из них также неудобно отсутствие модификаторов доступа, «свойств» в классах и разделение наследования интерфейса и реализации.
Да, прямое проецирование классического ООП на CLOS не работает или, по крайней мере, выглядит ужасно, но на это есть вполне конкретная причина, и она заключается в следующем: Методы отделены от классов не просто так.
Объектная парадигма CLOS фундаментально отличается от классической ООП, которая связана с передачей и обработкой сообщений.
CLOS смещает фокус с состояние И поведение субъекты своим взаимодействие друг с другом и с внешним миром.
Я выше писал, что в CLOS нет интерфейсов.
На самом деле это не совсем так — универсальные функции и есть интерфейсы, а их методы являются реализациями.
В этот момент кто-то из приверженцев функционального программирования может спросить: «Отлично, а чем это отличается от банального сопоставления с образцомЭ» Он отличается во многих отношениях.
Во-первых, специализация методов обобщенных функций возможна как по значению, так и по классу, причем в последнем случае с учетом наследования.
Во-вторых, методы можно добавлять к обобщенным функциям, причем даже прямо во время выполнения.
И в-третьих, диспетчеризацией можно управлять как угодно, назначая родовым функциям так называемые «комбинаторы методов», и это не говоря уже о декораторах :before, :after и :around для стандартного комбинатора.
О комбинаторах методов Я когда-то писал в своем ЖЖ , желающие могут прочитать, а здесь я покажу, как, используя один из встроенных комбинаторов, программа , реализуйте что-то похожее на «конструкторы» из обычных объектно-ориентированных языков: (defgeneric construct (object &key)
(:method-combination progn :most-specific-last))
(defun new (class &rest args &key &allow-other-keys)
(let ((object (make-instance class)))
(apply #'construct object args)
object))
(defclass superclass ()
(some-slot))
(defclass subclass (superclass)
())
(defmethod construct progn ((object superclass) &key value)
(setf (slot-value object 'some-slot) value)
(write-line "Superclass constructed"))
(defmethod construct progn ((object subclass) &key)
(write-line "Subclass constructed")
(format t "Slot value: ~s~%" (slot-value object 'some-slot)))
;; (new 'subclass :value 123)
;; ==> Superclass constructed
;; Subclass constructed
;; Slot value: 123
На мой взгляд, объектная модель CLOS органично синтезировала лучшее из ООП и функционального программирования и до сих пор является наиболее продвинутой реализацией представленной в ней парадигмы.
Обидно, что мейнстрим принимает ее идеи крайне медленно и неохотно, а воплощает их в жизнь еще хуже.
Теги: #Lisp #common lisp #clos #множественная диспетчеризация #мультиметоды #универсальные функции #множественная диспетчеризация #мультиметоды #универсальные функции #ООП #Lisp
-
Гугл Ответы
19 Oct, 24 -
Несколько Слов Об Удобстве Использования
19 Oct, 24 -
Wordpress 2.5: Редизайн
19 Oct, 24 -
Главный Динамик
19 Oct, 24 -
Вирусный Маркетинг В Действии
19 Oct, 24