2016-04-19 11 views
2

Я хотел бы контролировать способ сохранения значений в слотах и ​​то, что возвращается, когда я читаю слот. Вот мое определение класса:Common Lisp: как заменить слот-аксессоры?

(defclass object() 
    ((name :accessor name-access 
     :initform 'noname 
     :initarg :name) 
    (value :accessor value-access 
     :initform 10 
     :initarg :value))) 

Я создаю объект таким образом:

(setf obj1 (make-instance 'object)) 

Это так, как я получаю значение слота name:

(name-access obj1) 

, и как Я установил новое значение:

(setf (name-access obj1) 'realname) 

Каков правильный способ переопределить эту функцию (или метод) доступа, чтобы иметь возможность внести некоторые изменения в объект (при записи) и управлять возвращаемым значением?

спасибо.

ответ

5

Вы можете просто вручную определить методы для получения и установки слотов:

(defclass foo() 
    ((name :initform 'noname 
     :initarg :name))) 

(defgeneric name-access (foo) 
    (:method ((foo foo)) 
    (format t "~&Getting name.~%") 
    (slot-value foo 'name))) 

(defgeneric (setf name-access) (name foo) 
    (:method (name (foo foo)) 
    (format t "~&Setting a new name.~%") 
    (setf (slot-value foo 'name) name))) 

(defparameter *foo* (make-instance 'foo)) 
(name-access *foo*) 
; Getting name. 
;=> NONAME 

(setf (name-access *foo*) 'some-name) 
; Setting a new name. 
;=> SOME-NAME 

(name-access *foo*) 
; Getting name. 
;=> SOME-NAME 

Книга Practical Common Lisp проходит через них в chapter 17. Вы должны это прочитать.

5

Вы можете расширить методы доступа, определенные DEFCLASS:

CL-USER 66 > (defclass object() 
       ((name :accessor name-access 
         :initform 'noname 
         :initarg :name) 
       (value :accessor value-access 
         :initform 10 
         :initarg :value))) 
#<STANDARD-CLASS OBJECT 4220014953> 

письменной форме, используя :before метод:

CL-USER 67 > (defmethod (setf name-access) :before (new-value (o1 object)) 
       (print "hi")) 
#<STANDARD-METHOD (SETF NAME-ACCESS) (:BEFORE) (T OBJECT) 40202283BB> 

Чтение, с :around методом:

CL-USER 68 > (defmethod name-access :around ((o1 object)) 
       (let ((name (call-next-method))) 
       (values name (length (symbol-name name))))) 
#<STANDARD-METHOD NAME-ACCESS (:AROUND) (OBJECT) 4020061213> 

Пример :

CL-USER 69 > (let ((o1 (make-instance 'object))) 
       (setf (name-access o1) 'foobar) 
       (name-access o1)) 

"hi"  ; side effect 
FOOBAR ; return value 1 
6  ; return value 2 
+0

Спасибо. Кажется, что в некоторых сообществах это считается лучшим способом, чем переопределение аксессуаров. Но можно ли изменить возвращаемое значение с помощью методов ': before' или': around'? –

+0

@ andrei-n: см. Пример. Метод around возвращает все, что он хочет. Метод до этого не влияет. –

+0

@ andrei-n Изменение возвращаемого значения в методе around может вызвать проблемы для подклассов позже. Первичный метод всегда можно переопределить, когда требуется другое поведение, но нет простого способа избавиться от метода around. Лично я бы сказал, что методы before/around/after-good хороши, когда вам нужно добавить дополнительные функции к существующему методу, но определение основного метода должно быть предпочтительным для основных функций. – jkiiski