Common Lisp Object System (CLOS) — это средство объектно-ориентированного программирования в ANSI Common Lisp . CLOS — это мощная динамическая объектная система, которая радикально отличается от средств ООП, имеющихся в более статичных языках, таких как C++ или Java . CLOS была вдохновлена более ранними объектными системами Lisp, такими как MIT Flavors и CommonLoops , хотя она более общая, чем любая из них. Первоначально предложенная как дополнение, CLOS была принята как часть стандарта ANSI для Common Lisp и была адаптирована в другие диалекты Lisp, такие как EuLisp или Emacs Lisp . [1]
Основными строительными блоками CLOS являются методы , классы , экземпляры этих классов и универсальные функции . CLOS предоставляет макросы для их определения: defclass
, defmethod
, и defgeneric
. Экземпляры создаются с помощью метода make-instance
.
Классы могут иметь несколько суперклассов , список слотов (переменных-членов на языке C++/Java) и специальный метакласс . Слоты могут быть выделены по классу (все экземпляры класса совместно используют слот) или по экземпляру. Каждый слот имеет имя, и к значению слота можно получить доступ по этому имени с помощью функции slot-value
. Кроме того, могут быть определены специальные универсальные функции для записи или чтения значений слотов. Каждый слот в классе CLOS должен иметь уникальное имя.
CLOS — это система множественной диспетчеризации . Это означает, что методы могут быть специализированы по любому или всем их требуемым аргументам. Большинство ОО-языков являются однодиспетчерскими, что означает, что методы специализированы только по первому аргументу. Другая необычная особенность заключается в том, что методы не «принадлежат» классам; классы не предоставляют пространства имен для универсальных функций или методов. Методы определяются отдельно от классов и не имеют специального доступа (например, «this», «self» или «protected») к слотам класса.
Методы в CLOS сгруппированы в универсальные функции . Универсальная функция — это объект, который может быть вызван как функция и который связывает коллекцию методов с общим именем и структурой аргументов, каждый из которых специализирован для разных аргументов. Поскольку Common Lisp предоставляет классы non-CLOS для структур и встроенных типов данных (числа, строки, символы, символы, ...), диспетчеризация CLOS работает также с этими классами non-CLOS. CLOS также поддерживает диспетчеризацию по отдельным объектам (специализаторы eql). По умолчанию CLOS не поддерживает диспетчеризацию по всем типам данных Common Lisp (например, диспетчеризация не работает для полностью специализированных типов массивов или для типов, введенных deftype
). Однако большинство реализаций Common Lisp предоставляют протокол метаобъектов , который позволяет универсальным функциям предоставлять специфичную для приложения специализацию и правила диспетчеризации.
Диспетчеризация в CLOS также отличается от большинства объектно-ориентированных языков:
Этот механизм отправки работает во время выполнения. Добавление или удаление методов, таким образом, может привести к изменению эффективных методов (даже если общая функция вызывается с теми же аргументами) во время выполнения. Изменение комбинации методов также может привести к изменению эффективных методов.
Например,
; Объявляем прототип общей структуры аргумента. ( defgeneric f ( x y )) ; Определить реализацию для (f integer y), где y соответствует всем типам. ( defmethod f (( x integer ) y ) 1 ) ( ф 1 2.0 ) => 1 ; Определить реализацию для (f integer real). ( defmethod f (( x integer ) ( y real )) 2 ) ( f 1 2.0 ) => 2 ;Отправка изменена во время выполнения.
Как и OO-системы в большинстве динамических языков , CLOS не обеспечивает инкапсуляцию . Доступ к любому слоту можно получить с помощью slot-value
функции или через (опционально автоматически сгенерированные) методы доступа . Чтобы получить доступ к нему через , вам нужно знать имя слота. Программисты CL используют пакетнуюslot-value
возможность языка , чтобы объявить, какие функции или структуры данных предназначены для экспорта.
Помимо обычных («первичных») методов, существуют также :before
, :after
и :around
«вспомогательные» методы. Первые два вызываются до или после первичного метода в определенном порядке на основе иерархии классов. :around
Метод может контролировать, будет ли первичный метод выполняться вообще. Кроме того, программист может указать, следует ли вызывать все возможные первичные методы в иерархии классов или только тот, который обеспечивает наиболее близкое соответствие.
Стандартная комбинация методов обеспечивает основные, до, после и около методы, описанные выше. Существуют другие комбинации методов с другими типами методов. Могут быть определены новые (как простые, так и сложные) комбинации методов и типы методов.
CLOS допускает множественное наследование . Если порядок по умолчанию, в котором методы выполняются при множественном наследовании, неверен, программист может решить проблемы наследования ромба , указав порядок комбинаций методов.
CLOS является динамическим, что означает, что не только содержимое, но и структура его объектов могут быть изменены во время выполнения. CLOS поддерживает изменение определений классов на лету (даже если экземпляры рассматриваемого класса уже существуют), а также изменение членства в классе данного экземпляра с помощью change-class
оператора. CLOS также позволяет добавлять, переопределять и удалять методы во время выполнения. Проблема окружности-эллипса легко решается в CLOS, и большинство шаблонов проектирования ООП либо исчезают, либо качественно упрощаются. [2]
CLOS не является языком прототипов : классы должны быть определены до того, как объекты могут быть созданы как члены этого класса.
За пределами стандарта ANSI Common Lisp существует широко реализованное расширение CLOS, называемое Metaobject Protocol (MOP). MOP определяет стандартный интерфейс для основ реализации CLOS, рассматривая классы, описания слотов, обобщенные функции и методы как экземпляры метаклассов , и позволяет определять новые метаклассы и изменять все поведение CLOS. Гибкость CLOS MOP является прообразом аспектно-ориентированного программирования , которое позже было разработано некоторыми из тех же инженеров, такими как Грегор Кичзалес . MOP определяет поведение всей объектной системы с помощью набора протоколов. Они определены в терминах CLOS. Таким образом, можно создавать новые объектные системы, расширяя или изменяя предоставляемую функциональность CLOS. Книга The Art of the Metaobject Protocol описывает использование и реализацию CLOS MOP.
Различные реализации Common Lisp немного различаются по поддержке Meta-Object Protocol. Проект Closer [3] направлен на обеспечение недостающих функций.
Flavors (и его преемник New Flavors) были объектной системой на MIT Lisp Machine . Большие части операционных систем Lisp Machine и многие приложения для нее используют Flavors или New Flavors. Flavors представил множественное наследование и миксины , среди прочих функций. Flavors в основном устарел, хотя существуют реализации для Common Lisp. Flavors использовал парадигму передачи сообщений. New Flavors представил универсальные функции.
CommonLoops был преемником LOOPS (от Xerox Interlisp -D). CommonLoops был реализован для Common Lisp. Портативная реализация под названием Portable CommonLoops (PCL) была первой реализацией CLOS. PCL широко портируется и до сих пор является основой для реализации CLOS нескольких реализаций Common Lisp . PCL реализован в основном в портативном Common Lisp с несколькими системно-зависимыми частями.
Благодаря мощности и выразительности CLOS, а также исторической доступности Tiny CLOS (упрощенной переносимой реализации CLOS, написанной Грегором Кичалесом для использования со Scheme), основанные на MOP объектные системы типа CLOS стали фактической нормой в большинстве реализаций диалекта Lisp, а также нашли свое применение в ООП- возможностях некоторых других языков :