oop - 在 Common Lisp 对象系统中分离初始化参数和类槽以创建对象
问题描述
这询问从其他插槽初始化插槽。我想要实现的是将一些参数作为输入 - 也许但不一定make-instance
- 并将这些参数转换为类插槽以进行存储。实际上,我想将类的实现与其(初始化)接口分开。
有没有推荐的方法来实现这一目标?
我能想象的最简单的方法就是简单地创建一个(defun make-my-object ...)
作为接口。然后可以make-instance
使用适当的参数调用。
例如,想象
(defclass my-object () (slot-1 slot-2))
(defun make-my-object (arg-1 arg-2)
(make-instance 'my-object
:slot-1 (+ arg-1 arg-2)
:slot-2 (- arg-1 arg-2)))
我能想象的其他方法包括实现一个initialize-instance :after
将arg-1
and作为关键字参数并适当arg-2
初始化的方法。但是,由于 after 方法以最小特定的一阶调用,这意味着超类插槽将在当前类插槽之前初始化。另一方面,看起来更常见的是,将获取构造当前类的参数,并在这些参数的基础上初始化超类插槽。slot-1
slot-2
另一种选择是initialize-instance :before
- 或者也是:around
- 但是如果层次结构中的多个类具有这样的“接口实现”差异,我认为这不起作用,除非我可以将参数传递给call-next-method
.
还有其他方法吗?
编辑:感谢@ignis volens 让我注意到(其中一个)我主要关心的是从子类插槽初始化超类插槽。有推荐的方法吗?
解决方案
我不确定我是否理解你的问题。答案几乎可以肯定是在initialize-instance
我认为的方法之后。你说这会导致在超类中定义的槽首先被初始化:是的,它会,而且几乎可以肯定你想要发生的事情。超类中定义的插槽通常不依赖于它们在子类插槽上的值(它总是可以考虑所有事物的异常),因此以最不具体的一阶初始化几乎总是您想要的。
我使用的两种初始化槽的常用方法是简单地在定义中声明它们的 initargs 是什么:
(defclass minibeast ()
((legs :initform 'uncountable
:initarg :legs
:initarg :leg-count
:accessor legs)
(tentacles :initform 'many
:initarg :tentacles
:initarg :number-of-tentacles
:accessor tentacles)))
现在(make-instance 'minibeast :legs 87)
做你所期望的。这很有效(因为,如果两个插槽是在不同的类中定义的,显然它必须这样做):
(defclass awful-monster ()
((legs :initform 'uncountable
:initarg :legs
:initarg :leg-count
:accessor legs)
(appendages :initform 'many
:initarg :legs
:initarg :appendages)))
现在(make-instance 'awful-monster :legs 93)
将产生一个拥有 93 条腿和 93 个附肢的可怕怪物。
但是,该方法可能不符合将接口与实现分开的条件。您可能还想在初始化插槽时执行一些计算。在这两种情况下,方法后initialize-instance
通常是正确的方法:
(defclass horrible-monster ()
((legs :initform 983
:accessor legs)
(eyes :initform 63
:accessor eyes)
(appendages
:reader appendages)))
(defmethod initialize-instance :after
((m horrible-monster) &key eyes legs (stalky-eyes t))
(with-slots ((e eyes) (l legs) appendages) m
(when eyes (setf e eyes))
(when legs (setf l legs))
(setf appendages (if stalky-eyes (+ e l) l))))
现在可怕的怪物将获得适当数量的附属物(我不确定为什么可怕的怪物不知道他们的眼睛是否在茎上:也许他们没有镜子)。
当然,还有许多其他组合。您可能不希望make-instance
显式调用用户代码,而是将其封装在某个函数中:
(defun make-awful-thing (&rest args &key (sort-of-horrible-thing 'horrible-monster)
&allow-other-keys)
(let ((the-remaining-args (copy-list args)))
;; No doubt alexandria or something has a way of doing this
(remf the-remaining-args ':sort-of-horrible0thing)
(apply #'make-instance sort-of-horrible-thing the-remaining-args)))
现在,您当然可以轻松地拥有一些定制的初始化协议:
(defgeneric enliven-horrible-thing (horrible-thing &key)
(:method :around ((horrible-thing t) &key)
(call-next-method)
t))
(defun make-awful-thing (&rest args &key (sort-of-horrible-thing 'horrible-monster)
&allow-other-keys)
(let ((the-remaining-args (copy-list args)))
;; No doubt alexandria or something has a way of doing this
(remf the-remaining-args ':sort-of-horrible0thing)
(apply #'enliven-horrible-thing
(apply #'make-instance sort-of-horrible-thing
the-remaining-args)
the-remaining-args)))
(defmethod enliven-horrible-thing ((horrible-thing horrible-monster)
&key (ichor t) (smell 'unspeakable))
...)
推荐阅读
- python - 更新已安装的包而不生成 Pipfile.lock
- php - 如何根据管理员权限通过单个页面重定向所有用户?
- php - PHP中是否有任何特定于域的全局变量?
- qt - Linuxdeployqt,appimage创建成功,双击不运行
- reactjs - Material UI 中不同高度的列需要修复
- ios - 确保 @escaping 函数安全地从内存中删除
- mocking - 无法模拟具有 @RequiredArgsConstructor 的类
- php - 如何每行只有 3 个带有动态复选框编号的复选框
- r - R:使用 ggplot 更改风险热图中的 colorPalette 颜色
- android - 视图不想更新