Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Pol_Grem_-_ANSI_Common_Lisp_High_tech_-_2012.pdf
Скачиваний:
17
Добавлен:
12.03.2016
Размер:
4.85 Mб
Скачать

11

CLOS

Объект­ная­ систе­ма­ Common Lisp (Common Lisp Object System, CLOS) представ­ля­ет­ собой­ набор­ опера­то­ров­ для объект­но­-ориен­ти­ро­ван­но­го­ програм­ми­ро­ва­ния­. Всех их объеди­ня­ет­ исто­ри­че­ское­ прошлое­.° С тех­ ниче­ской­ точки­ зрения­ они ничем­ не отли­ча­ют­ся­ от осталь­но­го­ Com­ mon Lisp: defmethod – такая­ же неотъ­ем­ле­мая­ часть языка,­ как и defun.

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

Объект­но­-ориен­ти­ро­ван­ное­ програм­ми­ро­ва­ние ­– это опре­де­лен­ное­ изме­­ нение­ в орга­ни­за­ции­ программ­. Это изме­не­ние­ подоб­но­ тому,­ кото­рое­ произош­ло­ в распре­де­ле­нии­ процес­сор­но­го­ време­ни­. В 1970 году­­ под мно­ гополь­зо­ва­тель­ской­ систе­мой­ пони­мал­ся­ один или два больших­ мейн­ фрейма,­ с кото­ры­­ми соеди­ня­лось­ множе­ст­во­ простых­ терми­на­лов­. Те­ перь же под этим приня­то­ пони­мать­ большой­ набор­ рабо­чих­ станций,­ объеди­нен­ных­ в сеть. Таким­ обра­зом,­ вычис­ли­тель­ные­ мощно­сти­ те­ перь распре­де­ле­ны­ между­ инди­ви­ду­аль­ны­ми­ участ­ни­ка­ми­ сети,­ а не центра­ли­зо­ва­ны­ в одном­ большом­ компь­ю­те­ре­.

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

Допус­тим,­ мы хо­тим напи­сать­ програм­му,­ вычис­ляю­щую­ площадь­ раз­ нооб­раз­ных­ двумер­ных­ фигур­. Одним­ из подхо­дов­ могло­ бы быть напи­­ сание­ функции,­ кото­рая­ выяс­ня­ет­ тип объек­та­ и ведет­ себя­ соот­вет­ст­­ вующим­ обра­зом­. Пример­ такой­ функции­ приве­ден­ на рис. 11.1.

186

Глава 11. CLOS

(defstruct rectangle height width)

(defstruct circle radius)

(defun area (x)

(cond ((rectangle-p x)

(* (rectangle-height x) (rectangle-width x))) ((circle-p x)

(* pi (expt (circle-radius x) 2)))))

> (let ((r (make-rectangle))) (setf (rectangle-height r) 2

(rectangle-width r) 3) (area r))

6

Рис. 11.1. Представ­ле­ние­ площа­дей­ через­ структу­ры­ и функции­

С помо­щью­ CLOS мы можем­ пере­пи­сать­ код в другом­ ключе,­ как пока­­ зано­ на рис. 11.2. В объект­но­-ориен­ти­ро­ван­ной­ моде­ли­ програм­ма­ раз­ бива­ет­ся­ на несколь­ко­ мето­дов­, каждый­ из кото­рых­ предна­зна­чен­ для соот­вет­ст­вую­ще­го­ типа­ аргу­мен­та­. Два мето­да,­ пока­зан­ные­ на рис. 11.2, неяв­но­ опре­де­ля­ют­ функцию­ area, кото­рая­ будет­ вести­ себя­ так же, как анало­гич­ная­ функция­ на рис. 11.1. При вызо­ве­ area Лисп вызы­ва­ет­ опре­­ делен­­ный метод­ в соот­вет­ст­вии­ с типом­ аргу­мен­та­.

Поми­мо­ разбие­ния­ функций­ на мето­ды­ объект­но­-ориен­ти­ро­ван­ное­ про­ грамми­ро­ва­ние­ предпо­ла­га­ет­ насле­до­ва­ние­ – как для слотов,­ так и для мето­дов­. Пустой­ список,­ пере­дан­ный­ вторым­ аргу­мен­том­ в двух вызо­­ вах defclass (рис. 11.2), – это список­ супер­клас­сов­ (роди­тель­ских­ клас­ сов). Опре­де­лим­ теперь­ новый­ класс окра­шен­ных­ объек­тов,­ а затем­ класс окра­шен­ных­ кругов,­ имеющий­ два супер­клас­са:­ circle и colored:

(defclass colored () (color))

(defclass colored-circle (circle colored)

())

Созда­вая­ экзем­п­ля­ры­ класса­ colored-circle, мы увидим­ два вида­ насле­­ дова­ния:­

1.Экзем­п­ля­ры­ colored-circle будут­ иметь два слота:­ radius, насле­дуе­­ мый из класса­ circle, и color – из класса­ colored.

2.Так как для класса­ colored-circle свой метод­ не опре­де­лен,­ вызов­ area для данно­го­ класса­ будет­ исполь­зо­вать­ метод,­ опре­де­лен­ный­ для класса­ circle.

11.2. Классы и экземпляры

187

(defclass rectangle () (height width))

(defclass circle () (radius))

(defmethod area ((x rectangle))

(* (slot-value x ’height) (slot-value x ’width)))

(defmethod area ((x circle))

(* pi (expt (slot-value x ’radius) 2)))

> (let ((r (make-instance ’rectangle))) (setf (slot-value r ’height) 2

(slot-value r ’width) 3) (area r))

6

Рис. 11.2. Представ­ле­ние­ площа­дей­ через­ классы­ и мето­ды­

С практи­че­ской­ точки­ зрения­ под объект­но­-ориен­ти­ро­ван­ным­ програм­­ миро­ва­ни­ем­ подра­зу­ме­ва­ет­ся­ способ­ орга­ни­за­ции­ програм­мы­ в терми­­ нах мето­дов,­ классов,­ экзем­п­ля­­ров и насле­до­ва­ния­. Зачем­ может­ пона­­ добить­ся­ подоб­ная­ орга­ни­за­ция?­ Одно­ из заяв­ле­ний­ объект­но­-ориен­ти­­ рован­но­го­ подхо­да­ заклю­ча­ет­ся­ в том, что он упро­ща­ет­ изме­не­ния­ в програм­мах­. Если­ мы хотим­ изме­нить­ способ­ отобра­же­ния­ объек­тов­ класса­ ob, нам доста­точ­но­ внести­ изме­не­ния­ лишь в один метод­ display этого­ класса­. Если­ нам нужен­ новый­ класс объек­тов,­ похо­жих­ на ob, но слегка­ отли­чаю­щих­ся­ от них, мы можем­ создать­ подкласс­ ob, для кото­­ рого­ зада­дим­ необ­хо­ди­мые­ нам свойст­ва,­ а все осталь­ное­ унас­леду­ет­ся­ от роди­тель­­ского­ класса­. А если­ мы просто­ хотим­ полу­чить­ один объект­ ob, кото­рый­ ведет­ себя­ отлич­но­ от осталь­ных,­ мы можем­ создать­ ново­го­ по­томка­ ob и напря­мую­ поме­нять­ его свойст­ва­. Для внесе­ния­ изме­не­­ ний в грамот­но­ напи­сан­ную­ програм­му­ нам необя­за­тель­но­ даже­ смот­ реть на осталь­ной­ код.°

11.2.Классы и экземпляры

Вразделе 4.6 при созда­нии­ структур­ мы прохо­ди­ли­ два этапа:­ с помо­­ щью defstruct созда­ва­ли­ опре­де­ле­ние­ структу­ры,­ затем­ с помо­щью­ спе­ циаль­ной­ функции­ make-point созда­ва­ли­ саму­ структу­ру­. Созда­ние­ эк­ земп­ля­ров­ требу­ет­ двух анало­гич­ных­ шагов­. Для нача­ла­ опре­де­ля­ем­ класс с помо­щью­ defclass:

(defclass circle () (radius center))

Только­ что мы опре­де­ли­ли­ класс circle, кото­рый­ имеет­ два слота­ (по­ добно­ полям­ структу­ры),­ назван­ные­ radius и center.

188

Глава 11. CLOS

Чтобы­ создать­ экзем­п­ляр­ этого­ класса,­ вместо­ вызо­ва­ специ­фич­ной­ функции­ мы восполь­зу­ем­ся­ общей­ для всех классов­ функци­ей­ make-in­ stance, вызван­ной­ с именем­ класса­ в каче­ст­ве­ перво­го­ аргу­мен­та:­

> (setf c (make-instance ’circle)) #<Circle #XC27496>

Доступ­ к значе­нию­ слота­ можно­ осуще­ст­вить­ с помо­щью­ slot-value. Но­ вое значе­ние­ можно­ уста­но­вить­ с помо­щью­ setf:

> (setf (slot-value c ’radius) 1) 1

Как и для структур,­ значе­ния­ неини­циа­ли­зи­ро­ван­ных­ слотов­ не опре­­ деле­ны­.

11.3. Свойства слотов

Третий­ аргу­мент­ defclass должен­ содер­жать­ список­ опре­де­ле­ний­ слотов­. Простей­шим­ опре­де­ле­ни­ем­ слота,­ как в нашем­ приме­ре,­ служит­ символ,­ представ­ляю­щий­ имя слота­. В общем­ случае­ опре­де­ле­ние­ может­ быть списком,­ содер­жа­щим­ имя и набор­ свойств, пере­да­вае­мых­ по ключу­.

Пара­метр­ :accessor неяв­но­ созда­ет­ функцию,­ обеспе­чи­ваю­щую­ доступ­ к слоту,­ убирая­ необ­хо­ди­мость­ исполь­зо­вать­ slot-value. Если­ мы обно­­ вим наше­ опре­де­ле­ние­ класса­ circle следую­щим­ обра­зом:­

(defclass circle ()

((radius :accessor circle-radius) (center :accessor circle-center)))

то сможем­ ссылать­ся­ на слоты­ с помо­щью­ функций­ circle-radius и circ­ le-center соот­вет­ст­вен­­но:

>(setf c (make-instance ’circle)) #<Circle #XC5C726>

>(setf (circle-radius c) 1)

1

> (circle-radius c) 1

Если­ вместо­ :accessor исполь­зо­вать­ пара­мет­ры­ :writer или :reader, мож­ но задать­ по отдель­но­сти­ либо­ функцию­ уста­нов­ки­ значе­ния­ слота,­ ли­ бо функцию­ чтения­.

Исход­ное­ значе­ние­ слота­ опре­де­ля­ет­ся­ пара­мет­ром­ :initform. Чтобы­ можно­ было­ инициа­ли­зи­ро­вать­ слот в вызо­ве­ make-instance по ключу,­ нужно­ задать­ имя соот­вет­ст­вую­ще­го­ ключа­ как :initarg этого­ слота­.1 Опре­де­ле­ние­ класса,­ обла­даю­ще­го­ пере­чис­лен­­ными­ свойст­ва­ми,­ будет­ выгля­деть­ следую­щим­ обра­зом:­

1В каче­ст­ве­ имен исполь­зу­ют­ся,­ как прави­ло,­ ключе­вые­ слова,­ хотя­ это не явля­ет­ся­ обяза­тель­ным­ требо­ва­ни­ем­.

11.3. Свойства слотов

189

(defclass circle ()

((radius :accessor circle-radius :initarg :radius :initform 1)

(center :accessor circle-center :initarg :center :initform (cons 0 0))))

Теперь­ вновь создан­ный­ экзем­п­ляр­ будет­ иметь уста­нов­лен­­ные через­ :initform­ значе­ния­ слотов,­ если­ с помо­щью­ :initarg не зада­ны­ другие­ значе­ния:­

>(setf c (make-instance ’circle :radius 3)) #<Circle #XC2DE0E>

>(circle-radius c)

3

> (circle-center c) (0 . 0)

Обра­ти­те­ внима­ние,­ что :initarg имеет­ приори­тет­ перед­ :initform.

Слоты­ могут­ быть разде­ляе­мы­ми­, то есть имеющи­ми­ одина­ко­вое­ значе­­ ние для всех экзем­п­ля­ров­ класса­. Слот можно­ сделать­ разде­ляе­мым­ с помо­щью­ декла­ра­ции­ :allocation :class. (Другой­ возмож­ный­ вари­ант ­– :allocation :instance, но, посколь­ку­ это значе­ние­ исполь­зу­ет­ся­ по умол­ чанию,­ зада­вать­ его нет смысла­.) Если­ изме­нить­ значе­ние­ тако­го­ слота­ в одном­ экзем­п­ля­ре,­ это при­ведет­ к изме­не­нию­ значе­ния­ слота­ и во всех осталь­ных­ экзем­п­ля­рах­ данно­го­ класса­. Поэто­му­ исполь­зо­ва­ние­ разде­­ ляемых­ слотов­ обосно­ва­но­ в том случае,­ когда­ класс имеет­ свойст­во,­ одина­ко­вое­ для всех его экзем­п­ля­ров­.

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

(defclass tabloid ()

((top-story :accessor tabloid-story :allocation :class)))

Если­ какая­-либо­ тема­ попа­да­ет­ на первую­ страни­цу­ одно­го­ экзем­п­ля­ра­ таблои­да,­ она тут же попа­да­ет­ на первую­ страни­цу­ друго­го:­

> (setf daily-blab

(make-instance

’tabloid)

unsolicited-mail (make-instance

’tabloid))

#<Tabloid #XC2AB16>

 

 

>(setf (tabloid-story daily-blab) ’adultery-of-senator) ADULTERY-OF-SENATOR

>(tabloid-story unsolicited-mail)

ADULTERY-OF-SENATOR

Тут вы можете оставить комментарий к выбранному абзацу или сообщить об ошибке.

Оставленные комментарии видны всем.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]