クラスに必須スロットを設定する - lisp-cookbook-ja/common-lisp GitHub Wiki
MOPを利用しない場合
:initformにエラーを発する式を与えることにより、初期化時に明示的に上書きしない場合、エラーを発します。
(defclass foo ()
((r1 :initform (error "Required slot unbound R1.") :initarg :r1)
(r2 :initform (error "Required slot unbound R2.") :initarg :r2)
(r3 :initform (error "Required slot unbound R3.") :initarg :r3)
(x :initarg :x)))
動作
(make-instance 'foo)
;>>> Required slot unbound R1.
(describe (make-instance 'foo :r1 1 :r2 2 :r3 3))
;-> #<FOO {104A159393}>
; [standard-object]
;
; Slots with :INSTANCE allocation:
; R1 = 1
; R2 = 2
; R3 = 3
; X = #<unbound slot>
;
;=> No value.
MOPを利用した場合
下記では、required-slot-classという必須スロットのメタクラスを作成し、make-instance時にチェックし条件が満されていない場合にエラーを発します。
MOPのコンパチブルレイヤーとしてCLiki:Closer-MOPを利用します
導入方法
(ql:quickload :closer-mop)
(defclass required-slot-class (standard-class)
((direct-required-slots
;; メタクラスの:initargは、defclassのslot-optionとして利用できる
:initarg :required-slots
:initform '()
:reader direct-required-slots)
(class-required-slots :accessor class-required-slots)))
(defmethod c2mop:validate-superclass ((class required-slot-class)
(super standard-class))
;; standard-class、required-slot-classの異なるメタクラス間で
;; クラスが継承できるようにする
t)
(defmethod direct-required-slots ((class class))
;; required-slot-classでない場合は空リストを返す
'())
(defmethod c2mop:finalize-inheritance :after ((class required-slot-class))
;; class-precedence-listを継承順に辿り、direct-required-slotsをマージして
;; class-required-slotsを作成する
(setf (class-required-slots class)
(remove-duplicates (mapcan (lambda (ds)
(copy-list (direct-required-slots ds)))
(c2mop:class-precedence-list class))
:from-end T
:test #'equal)))
(defmethod unbound-required-slot-using-class ((class required-slot-class) object slot-name)
;; required-slotがunboundの場合エラーを発する
(error "Required slot unbound ~A." slot-name))
(defmethod check-required-slots-using-class ((class required-slot-class) object)
;; required-slotのクラスとインスタンスを用いてrequired-slotがunboundでないかを
;; チェックする
(dolist (rs (class-required-slots class))
(unless (and (symbolp rs) (slot-boundp object rs))
(unbound-required-slot-using-class class object rs))))
(defmethod make-instance ((class required-slot-class) &rest args)
;; インスタンスを生成し、スロットをcheck-required-slots-using-classでチェックする
(let ((instance (call-next-method)))
(check-required-slots-using-class class instance)
instance))
動作
(defclass required-slots-demo ()
((r1 :initarg :r1) ;必須1
(r2 :initarg :r2) ;必須2
(r3 :initarg :r3) ;必須3
(x :initarg :x)
(y :initarg :y)
(z :initarg :z))
(:metaclass required-slot-class)
(:required-slots r1 r2 r3)) ;必須スロット名を指定
(describe (make-instance 'required-slots-demo :r1 1 :r2 2 :r3 3))
;-> #<REQUIRED-SLOTS-DEMO {104BD22F33}>
; [standard-object]
;
; Slots with :INSTANCE allocation:
; R1 = 1
; R2 = 2
; R3 = 3
; X = #<unbound slot>
; Y = #<unbound slot>
; Z = #<unbound slot>
;
;=> No value
(make-instance 'required-slots-demo :r2 2 :r3 3 :x 4)
;>>> Required slot unbound R1.
参考
- オブジェクト指向コンピューティング (岩波コンピュータサイエンス) P153〜P188