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

11.7. Вспомогательные методы

195

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

(defmethod combine ((x (eql ’powder)) (y (eql ’spark))) ’kaboom)

мы тем самым­ пере­оп­ре­де­лим­ пове­де­ние­ combine для аргу­мен­тов­ powder

иspark.

11.7.Вспомогательные методы

Мето­ды­ могут­ допол­нять­ся­ вспомо­га­тель­ны­ми­ мето­да­ми­, включая­ be­ fore­- (перед),­ after- (по­сле) и around-мето­ды­ (вокруг)­ . Before-мето­ды­ по­ зволя­ют­ нам сказать:­ «Преж­де­ чем присту­пить­ к выпол­не­нию,­ сделай­те­ это». Они вызы­ва­ют­ся­ в поряд­ке­ убыва­ния­ специ­фич­но­сти,­ предва­ряя­ вызов­ основ­но­го­ мето­да­. After-мето­ды­ позво­ля­ют­ сказать:­ «P.S. А сде­ лайте­ заод­но­ и это». Они вызы­ва­ют­ся­ после­ выпол­не­ния­ основ­но­го­ мето­­ да в поряд­ке­ увели­че­ния­ специ­фич­но­сти­. То, что вы­полня­ет­ся­ между­ ними,­ ранее­ назы­­валось­ нами­ просто­ мето­дом,­ но более­ точно­ это – пер­ вичный­ метод­. В резуль­та­те­ вызо­ва­ возвра­ща­ет­ся­ именно­ его значе­ние,­ даже­ если­ после­ него­ были­ вызва­ны­ after-мето­ды­.

Before- и after-мето­ды­ позво­ля­ют­ добав­лять­ новое­ пове­де­ние­ к вызо­ву­ первич­но­го­ мето­да­. Around-мето­ды­ позво­ля­ют­ вы­полнять­ то же самое,­ но более­ ради­каль­ным­ обра­зом­. Если­ задан­ around-метод,­ он будет­ вы­ зван вместо­ первич­но­го­. Впрочем,­ по своему­ усмот­ре­нию­ он может­ вы­ звать первич­ный­ метод­ само­стоя­тель­но­ (с помо­щью­ функции­ call-next- method, кото­рая­ предна­зна­че­на­ именно­ для этой цели)­ .

Этот меха­низм­ назы­­вает­ся­ стандарт­ной­ комби­на­ци­ей­ мето­дов­. Со­ гласно­ ему вызов­ обобщен­ной­ функции­ включа­ет:­

1.Вызов­ наибо­лее­ специ­фич­но­го­ around-мето­да,­ если­ задан­ хотя­ бы один из них.

2.В против­ном­ случае­ по поряд­ку:­

(a)Все before-мето­ды­ в поряд­ке­ убы­вания­ специ­фич­но­сти­.

(b)Наибо­лее­ специ­фич­ный­ первич­ный­ метод­.

(c)Все after-мето­ды­ в поряд­ке­ возрас­та­ния­ специ­фич­но­сти­.

Возвра­щае­мым­ значе­ни­ем­ счита­ет­ся­ значе­ние­ around-мето­да­ (в случае­ 1) или значе­ние­ наибо­лее­ специ­фич­но­го­ первич­но­го­ мето­да­ (в случае­ 2).

Вспомо­га­тель­ные­ мето­ды­ зада­ют­ся­ указа­ни­ем­ ключа­-квали­фи­ка­то­ра­ (qualifier) после­ имени­ мето­да­ в опре­де­ле­нии­ defmethod. Если­ мы опре­де­­ лим первич­ный­ метод­ speak для класса­ speaker следую­щим­ обра­зом:­

(defclass speaker () ())

(defmethod speak ((s speaker) string)

196

Глава 11. CLOS

(format t "~A" string))

то вызов­ speak для экзем­п­ля­ра­ speaker просто­ напе­ча­та­ет­ второй­ аргу­­ мент:

> (speak (make-instance ’speaker) "I’m hungry")

I’m hungry NIL

Опре­де­ляя­ подкласс­ intellectual, обора­чи­ваю­щий­ before- и after-мето­­ ды вокруг­ первич­но­го­ мето­да­ speak:

(defclass intellectual (speaker) ())

(defmethod speak :before ((i intellectual) string) (princ "Perhaps "))

(defmethod speak :after ((i intellectual) string) (princ " in some sense"))

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

> (speak (make-instance ’intellectual) "I’m hungry")

Perhaps I’m hungry in some sense NIL

Как было­ упомя­ну­то­ выше,­ вызы­­вают­ся­ все before- и after-мето­ды­. Ес­ ли мы теперь­ опре­де­лим­ before- и after-мето­ды­ для супер­клас­са­ speaker:

(defmethod speak :before ((s speaker) string) (princ "I think "))

то они будут­ вызы­­ваться­ из сере­ди­ны­ наше­го­ «сэндви­ча»:­

> (speak (make-instance ’intellectual) "I’m hungry")

Perhaps I think I’m hungry in some sense NIL

Неза­ви­си­мо­ от того,­ вызы­­вают­ся­ ли before- и after-мето­ды,­ при вызо­ве­ обобщен­ной­ функции­ всегда­ возвра­ща­ет­ся­ значе­ние­ первич­но­го­ мето­­ да. В нашем­ случае­ format возвра­ща­ет­ nil.

Это утвер­жде­ние­ не распро­стра­ня­ет­ся­ на around-мето­ды­. Они вызы­ва­­ ются­ сами­ по се­бе, а все осталь­ные­ мето­ды­ вызы­ва­ют­ся,­ лишь если­ им позво­лит­ around-метод­. Aroundили первич­ный­ мето­ды­ могут­ вызы­вать­ следую­щий­ метод­ с помо­щью­ call-next-method. Перед­ этим умест­но­ про­ верить­ нали­чие­ тако­го­ мето­да­ с помо­щью­ next-method-p.

С помо­щью­ around-мето­дов­ вы може­те­ опре­де­лить­ другой,­ более­ преду­­ преди­тель­ный­ подкласс­ speaker:

(defclass courtier (speaker) ())

11.8. Комбинация методов

197

(defmethod speak :around ((c courtier) string)

(format t "Does the King believe that ~A? "

string)

(if (eql (read) ’yes)

 

(if (next-method-p) (call-next-method))

 

(format t "Indeed, it is a preposterous

idea.~%"))

’bow)

 

Когда­ первым­ аргу­мен­том­ speak явля­ет­ся­ экзем­п­ляр­ класса­ courtier (придвор­ный),­ языком­ придвор­но­го­ управля­ет­ around-метод:­

>(speak (make-instance ’courtier) "kings will last") Does the King believe that kings will last? yes

I think kings will last BOW

>(speak (make-instance ’courtier) "the world is round") Does the King believe that the world is round? no Indeed, it is a preposterous idea.

BOW

Еще раз обра­ти­те­ внима­ние,­ что, в отли­чие­ от before- и after-мето­дов,­ значе­ни­ем­ вызо­ва­ обобщен­ной­ функции­ явля­ет­ся­ возвра­щае­мое­ значе­­ ние around-мето­да­.

11.8. Комбинация методов

Единст­вен­­ный первич­ный­ метод,­ кото­рый­ будет­ вызы­­ваться­ в стан­ дартной­ комби­на­ции,­ явля­ет­ся­ наибо­лее­ специ­фич­ным­ (при этом он может­ вызы­­вать другие­ мето­ды­ с помо­щью­ call-next-method). Но иногда­ нам хоте­лось­ бы иметь возмож­ность­ комби­ни­ро­вать­ резуль­та­ты­ всех подхо­дя­щих­ по прави­лам­ выбо­ра­ первич­ных­ мето­дов­.

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

(defun price (&rest args)

 

(+ (apply наибо­лее­ специ­фич­ный­ первич­ный­ метод­

args)

.

 

.

 

.

 

(apply наименее­ специ­фич­ный­ первич­ный­ метод­

args)))

Если­ суще­ст­ву­ют­ приме­ни­мые­ around-мето­ды,­ то они вызы­­вают­ся­ в по­ рядке­ очеред­но­сти,­ как в стандарт­ной­ комби­на­ции­. В опера­тор­ной­ ком­ бина­ции­ around-метод­ по-преж­нему­ может­ исполь­зо­вать­ call-next-me­ thod, но уже не может­ вызы­­вать первич­ные­ мето­ды­.

198

Глава 11. CLOS

Исполь­зуе­мый­ способ­ комби­на­ции­ мето­дов­ может­ быть задан­ в момент­ явно­го­ созда­ния­ обобщен­ной­ функции­ через­ defgeneric:

(defgeneric price (x) (:method-combination +))

Теперь­ метод­ price будет­ исполь­зо­вать­ + для комби­на­ции­ мето­дов;­ вто­ рой аргу­мент­ defmethod-опре­де­ле­ния­ должен­ содер­жать­ +. Опре­де­лим­ неко­то­рые­ классы­ с цена­ми:­

(defclass jacket () ()) (defclass trousers () ())

(defclass suit (jacket trousers) ())

(defmethod price + ((jk jacket)) 350) (defmethod price + ((tr trousers)) 200)

Теперь­ если­ мы запро­сим­ цену­ костю­ма­ (suit), то полу­чим­ сумму­ приме­­ нимых­ мето­дов­ price:

> (price (make-instance ’suit)) 550

Пара­метр­ :method-combination, пере­да­вае­мый­ вторым­ аргу­мен­том­ defge­ neric (а также­ вторым­ аргу­мен­том­ defmethod), может­ быть одним­ из сле­ дующих­ симво­лов:­

+

and

append list

max

min

nconc

or progn

Также­ мож­но исполь­зо­вать­ символ­ standard, кото­рый­ явным­ обра­зом­ зада­ет­ исполь­зо­ва­ние­ стандарт­ной­ комби­на­ции­ мето­дов­.

Опре­де­лив­ едино­жды­ способ­ комби­на­ции­ для обобщен­ной­ функции,­ вы выну­ж­де­ны­ исполь­зо­вать­ этот способ­ для всех мето­дов,­ имеющих­ то же имя. Попыт­ка­ исполь­зо­вать­ другие­ симво­лы­ (а также­ :before и :after) приве­дет­ к возник­но­ве­нию­ ошибки­. Чтобы­ изме­нить­ способ­ комби­на­­ ции price, вам придет­ся­ удалить­ саму­ обобщен­ную­ функцию­ с помо­­ щью fmakunbound.

11.9. Инкапсуляция

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

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

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