IDL 对象编程:继承(Inherits)与包含(Contains)
14783 为本文评分:
3.0
IDL 对象编程:继承(Inherits)与包含(Contains)
匿名 2015年7月9日,星期四
上个月,我写了一篇名为《在 IDL 中重载对象》的文章。那篇文章讨论了对象如何继承一个类并成为该类的子类。当一个对象继承另一个类时,该对象被认为是其父类之一。在上个月博客中的 Fancy_List 示例中,通过调用 ISA 函数,询问 IDL 这个 Fancy_List 是否是一个列表(list)来证明了这一点。IDL 返回了 true。
然而,有时候继承并不合适。例如,我希望我的 fancy list 包含一个哈希(HASH),用于存储列表的修改历史;哈希的键将包含列表发生更改时的时间戳,哈希的值将是哈希的最新版本。
在这个例子中,让 fancy list 继承 HASH 是没有意义的。首先,fancy list 不应该具有 HASH 的属性和方法。其次,我们也不希望说我们的 fancy list 是一个哈希。最后,因为 LIST 和 HASH 都是 IDL_Object 的子类,而 IDL 不允许 IDL_Object 被多重继承,所以下面这行代码会报错:
!null = {Fancy_List, inherits List, inherits hash}
% FANCY_LIST 已被定义,且定义冲突。 % 模块 FANCY_LIST__DEFINE 中出现 1 个编译错误。
相反,fancy list 应该包含(contain) 一个哈希,而不是直接继承它。下面的对象定义创建了一个名为 "history" 的成员变量:
**pro Fancy_List__Define
!null = {Fancy_List, $ inherits List, $ history: Object_New() }
end**
请注意,这只是为哈希创建了一个占位符。哈希必须在 ::Init 方法中定义。
**function fancy_list::Init, _REF_EXTRA=extra
if (~self.List::Init(_EXTRA=extra)) then return, 0
self.history = OrderedHash()
return, 1
end**
现在,正如预期的那样,调用 ISA(fl, 'HASH') 返回零。
更新成员
每次列表被修改时,哈希都需要更新。下面是一个 ::Add 方法的示例,它在每次添加值时更新历史记录。
**pro Fancy_List::Add, value, index, _REF_EXTRA=extra
if ~Isa(value, /NUMBER) then return
self.List::Add, value, index, _EXTRA=extra
self.history[timestamp()] = self[0:self.Count()-1]
end**
(操作 self[0:self.Count()-1] 会创建包含所有元素的列表的子列表,这是一种复制列表的巧妙方法)
类似的逻辑应该添加到其他修改方法中,如 ::Remove, ::Move 等。
从对象外部检索成员
现在可以使用以下调用创建带有历史记录的 fancy list:
fl = Obj_New('Fnacy_List')
那么如何访问其中的哈希呢?
哈希可以是对象的一个属性,也可以通过一个单独的方法访问。通常,使用属性就足够了,除非在将成员传递到对象外部之前需要对其执行额外的工作。
pro Fancy_List::GetProperty, MODIFICATION_HISTORY=history, _REF_EXTRA=extra
**if (Arg_Present(history)) then begin history = self.history endif
if (N_Elements(extra) gt 0) then begin ; 从超类获取未在此类中定义的任何属性。 self.List::GetProperty, _EXTRA=extra endif
end**
使用属性的一个便利之处是,可以使用"点"语法来访问:
history = fl.MODIFICATION_HISTORY
从类外部修改成员
与通过 GetProperty 访问成员类似,通常也能够通过 SetProperty 来设置成员。然而,在这个例子中,历史记录不应该包含在 SetProperty 中,因为它应该只在对象内部追加,而不允许从外部修改。
用户可能想做的一件事是偶尔清除历史记录以节省内存。这是说明应如何通过单独的方法修改成员的一个很好的例子。Fancy_List::ClearHistory 是个好名字。
**pro Fancy_List::ClearHistory
self.history.Remove, /ALL
end**
清理成员
最后,了解当包含成员的对象被销毁时,应如何处理成员变量很重要。在许多情况下,被包含的项会在其超出作用域时被自动垃圾回收,所以无需担心。
然而,编写一个处理成员清理的 ::Cleanup 方法通常是良好的实践。这确保了无论该成员在类外部的何处被使用(它有可能陷入循环引用),它都能被妥善处置。此外,可能有些情况下,你希望在对象销毁时显式清理该成员或对其执行特殊处理。
下面是 Fancy_List 的一个 Cleanup 方法示例:
**pro Fancy_List::Cleanup
Obj_Destroy, self.history
end**
下面演示了当父对象被销毁时,历史记录也被销毁的情况:
Obj_Destroy, fl print, obj_Valid(history)
IDL 打印:
0