Объектно-ориентированное программирование

  При программировании больших и сложных задач требуется большое количество программистов и такое их количество столкнулось с организационной проблемой. Объектно-ориентированный стиль программирования позволяет пользоваться только маленьким количеством функций и не знать устройство большой системы.
  На ранних этапах развития было очень много разновидностей такой системы. Замыкания были как одна функция с внутренними переменными. Происхождение термина "замыкание" объясняется тем, что замыкание запоминает статические связи свободных переменных и не позволяет на них влиять внешним по отношению к данному замыканию функциям. Переменные замыкания невозможно использовать извне замыкания, и изменения их значений извне не видны.
  Дальнейшим развитием было объектно-ориентированное программирование, где объект это небольшая структура данных, которые можно использовать с помощью небольшого количества функций (методов). Определение объектов задаётся с помощью классов, где все объекты имеют одинаковое количество внутренних переменных и методы задаются в единственном числе для всех объектов данного класса.

Генератор порождает пошаговые значения

  Объект хорошо подходит для программировании генераторов. Под генератором понимают объект, из которого можно получать новые, отличные от прежних значения. При каждом вызове генератор порождает значение некоторого типа, которое является в некотором смысле следующим по порядку. Генераторам присуща следующая особенность: значения порождаются только при необходимости и формирование следующего значения основано на предыдущем. Приведём пример:

>(('натуральное-число defclass) defvar x) ; определение внутренней переменной
натуральное-число
>('натуральное-число defmethod натуральное-число () ('x set 0))
; конструктор
(lambda nil ('x set 0))
>('натуральное-число defmethod следующий ()
; метод который можно вызывать для всех объектов
  ('x set (x + 1)))
(lambda nil ('x set (x + 1)))
>('число set ('натуральное-число newobject))
; новый объект данного класса
натуральное-число

  Генератор чисел с помощью метода следующий порождает при каждом вызове целое число на единицу больше, чем порождённое в предыдущем вызове.

>(число следующий)
1
>(число следующий)
2

  Можно создать различные экземпляры генератора, дать новое начальное значение и дать экземпляру имя:

>('натуральное-число defmethod натуральное-число (x0) ; конструктор имеет имя класса и вызывается при создании нового объекта
  ('x set x0))
(lambda (x0) ('x set x0))
>('другое-число set ('натуральное-число newobject 10))
натуральное-число
>(другое-число следующий)
11
>(число следующий)
3

Статические члены

  Большая проблема при программировании это количество общих глобальных переменных. Если все объекты пользуются одним набором переменных, то возникают много неприятных сюрпризов, и программирование становится чрезмерно сложным. Возможно, "всего лишь одна маленькая глобальная переменная" не станет слишком неуправляемой, но подобный стиль приводит к коду, который бесполезен для всех, кроме программиста, написавшего его.
  Мы можем сохранить удобство значения по умолчанию и избежать неприятностей из-за появления глобальной переменной. Переменная, которая является частью класса, но не является частью объекта этого класса, называется статическим членом. Существует ровно одна копия статического члена, в отличии от обычных членов, когда каждый объект класса имеет свои независимые члены.
  Приведём пример конструктора объекта который пользуется значением по умолчанию, используя статический член объекта:

>('натуральное-число defstatic 'начальное-значение 5)
натуральное-число
>('натуральное-число defmethod натуральное-число ()
  ('x set начальное-значение))
(lambda nil ('x set начальное-значение))
>('число set ('натуральное-число newobject))
натуральное-число
>(число следующий)
6

Ссылка на себя

  При реализации методов ссылка на себя задаётся переменной this. Эта переменная требуется для повторного запуска своего метода внутри метода. Также данное значение можно отдать как значение аргумента другим функциям. Данная переменная необычная, не рекомендуется менять её значение, в основном используется для чтения.
  Можно в методах возвращать себя для удобства записей.

>('натуральное-число defmethod значение () x)
(lambda nil x)
>(число значение)
6
>('натуральное-число defmethod ++ ()
  ('x set (x + 1)) this)
(lambda nil ('x set (x + 1)) this)
>(((число ++) ++) ++)
натуральное-число
>(число значение)
9

Производные классы

  Допустим, мы создаём программу, обрабатывающую информацию о сотрудниках фирмы. Для такой программы может понадобиться следующий класс:

>(('служащий defclass) defvar имя фамилия дата-найма отдел)
служащий

  Затем мы можем попытаться определить менеджера:

>(('менеджер defclass) defvar служащий ; сведения о менеджере как о сотруднике
  группа ; подчинённые
  уровень)
менеджер

  Менеджер также является сотрудником; соответствующие данные хранятся в члене служащий объекта менеджер. Внимательно читающему человеку это может быть и очевидно, но здесь ничто не говорит программе, что менеджер является в то же время и служащий. Правильный подход - явно указать, что менеджер является сотрудником.

>(('менеджер defclass служащий) defvar группа уровень)
менеджер

  Класс менеджер является производным от служащий, а служащий является базовым классом для менеджер. Класс менеджер кроме своих собственных членов (группа и уровень) содержит члены класса служащий (имя, отдел и т.д.).

Методы

  Простые классы, такие как служащий и менеджер, как правило не представляют большого интереса и не особенно полезны. То что нам требуется - это представление информации в виде подходящего типа с соответствующим набором операций, отражающих концепцию. Мы хотим это сделать, не вникая в детали конкретной реализации. Например:

>(('служащий defclass) defvar имя фамилия …)
служащий
>('служащий defmethod полное-имя ()
  (имя + #\space фамилия))
(lambda nil (имя + #\space фамилия))
>('служащий defmethod writeln ()
  (cout writeln (this полное-имя)))
(lambda nil (cout writeln (this полное-имя)))

  Член производного класса может пользоваться методами базового класса так, как будто они объявлены в самом производном классе. Например:

('менеджер defmethod writeln ()
  (cout writeln "Имя: " (this полное-имя))

  Для удобства можно использовать методы базового класса, указывая полное имя метода :

('менеджер defmethod writeln ()
  (cout write "Имя: ") (this служащий::writeln)
  (cout writeln "Уровень: " уровень))

Личные методы

  Для внутреннего обслуживания работы объекта нужны методы закрытого доступа. Эти методы могут вызываться только внутри работы метода. Данная возможность реализована только с помощью определения статического члена со значением как lambda-список.

('менеджер defstatic 'private-method '(lambda ()
  top-secret)

  Вызывать такую функцию можно только внутри работы метода используя eval, funcall, appply.