17.3 定义对象 (Defining Objects)

第一个我们需要改善的是,写一个用来创建对象的函数。我们程序表示对象以及其父类的方式,不需要给用户知道。如果我们定义一个函数来创建对象,用户将能够一个步骤就创建出一个对象,并指定其父类。我们可以在创建一个对象的同时,顺道构造优先级列表,而不是在每次当我们需要找一个属性或方法时,才花费庞大代价来重新构造。

如果我们要维护优先级列表,而不是在要用的时候再构造它们,我们需要处理列表会过时的可能性。我们的策略会是用一个列表来保存所有存在的对象,而无论何时当某些父类被改动时,重新给所有受影响的对象生成优先级列表。这代价是相当昂贵的,但由于查询比重定义父类的可能性来得高许多,我们会省下许多时间。这个改变对我们的程序的灵活性没有任何影响;我们只是将花费从频繁的操作转到不频繁的操作。

图 17.4 包含了新的代码。 λ 全局的 *objs* 会是一个包含所有当前对象的列表。函数 parents 取出一个对象的父类;相反的 (setf parents) 不仅配置一个对象的父类,也调用 make-precedence 来重新构造任何需要变动的优先级列表。这些列表与之前一样,由 precedence 来构造。

用户现在不用调用 make-hash-table 来创建对象,调用 obj 来取代, obj 一步完成创建一个新对象及定义其父类。我们也重定义了 rget 来利用保存优先级列表的好处。

  1. (defvar *objs* nil)
  2. (defun parents (obj) (gethash :parents obj))
  3. (defun (setf parents) (val obj)
  4. (prog1 (setf (gethash :parents obj) val)
  5. (make-precedence obj)))
  6. (defun make-precedence (obj)
  7. (setf (gethash :preclist obj) (precedence obj))
  8. (dolist (x *objs*)
  9. (if (member obj (gethash :preclist x))
  10. (setf (gethash :preclist x) (precedence x)))))
  11. (defun obj (&rest parents)
  12. (let ((obj (make-hash-table)))
  13. (push obj *objs*)
  14. (setf (parents obj) parents)
  15. obj))
  16. (defun rget (prop obj)
  17. (dolist (c (gethash :preclist obj))
  18. (multiple-value-bind (val in) (gethash prop c)
  19. (if in (return (values val in))))))

图 17.4:创建对象