这是 Rainier 示例的放大版,它增加了一个小转折:通过约束槽值进行专业化(子类化),至少在构建时是这样。考虑一个用于二维矩形的类m-box :
(defclass m-box ()
((left :accessor m-box-left :initform 0 :type integer :initarg :left )
(top :accessor m-box-top :initform 0 :type integer :initarg :top )
(width :accessor m-box-width :initform 0 :type integer :initarg :width )
(height :accessor m-box-height :initform 0 :type integer :initarg :height)))
我们可以这样尝试:
(describe (make-instance 'm-box :left 42 :top -39 :width 5 :height 11))
: #<M-BOX {10047A8F83}>
: [standard-object]
:
: Slots with :INSTANCE allocation:
: LEFT = 42
: TOP = -39
: WIDTH = 5
: HEIGHT = 11
现在考虑一个子类或特化:让一个m-block是一个具有单位宽度和高度的 m-box。我们将initialize-instance方法设置为传递left和top的值,但不传递width和height:
(defclass m-block (m-box) ())
(defmethod initialize-instance
:around
((mb m-block)
&key (left 0) (top 0))
(call-next-method mb :left left :top top :width 1 :height 1))
我们可以如下创建一个m-block的实例:
(describe (make-instance 'm-block :left 17 :top -34 :width 5 :height 11))
: #<M-BLOCK {10049C0CC3}>
: [standard-object]
:
: Slots with :INSTANCE allocation:
: LEFT = 17
: TOP = -34
: WIDTH = 1
: HEIGHT = 1
构造函数不会阻止用户设置width和height的尝试,就像对一些不存在的插槽所做的那样:
(describe (make-instance 'm-block :left 17 :top -34 :plugh 2345))
Invalid initialization argument:
:PLUGH
in call for class #<STANDARD-CLASS COMMON-LISP-USER::M-BLOCK>.
[Condition of type SB-PCL::INITARG-ERROR]
但构造函数确实用 1 更正了用户的无效输入。
error
如果用户尝试输入无效的宽度或高度,您可能希望通过调用使模型更加紧密:
(defclass m-block (m-box) ())
(defmethod initialize-instance
:around
((mb m-block)
&key (left 0) (top 0) (width 1) (height 1))
(when (or (/= 1 width) (/= 1 height))
(error "an m-block must have unit width and height"))
(call-next-method mb :left left :top top :width 1 :height 1))
用户的以下尝试被拒绝:
(describe (make-instance 'm-block :left 17 :top -34 :width 5 :height 11))
an m-block must have unit width and height
[Condition of type SIMPLE-ERROR]
但是这个也演示了height的默认设置,它经历了:
(describe (make-instance 'm-block :left 17 :top -34 :width 1))
: #<M-BLOCK {1005377983}>
: [standard-object]
:
: Slots with :INSTANCE allocation:
: LEFT = 17
: TOP = -34
: WIDTH = 1
: HEIGHT = 1
这个例子允许用户在setf
之后调整宽度或高度。我不知道如何在子类的实例中而不是在超类的实例中使宽度和高度只读。