以下文字源自我对源代码的理解,如有不同意见,欢迎留言讨论或发邮件(
xuruilong100@163.com
)
QuantLib 金融计算——原理之 Quote、Handle 与观察者模式
QuantLib 中的观察者模式
QuantLib 的设计初衷是提供一个生产环境下的实时计算框架,而不是作为一个教学工具。生产环境中的计算任务通常面临一个非常现实的诉求,即需要某种机制可以触发自动计算,而设计模式中的观察者模式通常可以用来实现这一功能。正是实时计算的要求,要保证所有的计算都在相同的估值日期发生,这也就是为什么作为单体模式实现的 Settings
中存在 evaluationDate
方法。
QuantLib 中的观察者模式有两个类合作实现,一个是 Observer
,另一个是 Observalbe
,两个类扮演的角色不言自明。
Observer
和 Observalbe
是非常底层的基类,用户最常打交道的其实是另外两个类,一个是 SimpleQuote
,它是 Quote
的派生类(Quote
是 Observalbe
的派生类);另一个是类模板 Handle
,Handle
借助内部类 Link
同时扮演观察者和被观察对象的角色,也就是充当“传令官”。
SimpleQuote
的实现很简单,可以把它理解成为一个带有通知功能的智能浮点数。
Handle
在构造具体实例的时候会注册成为一个 SimpleQuote
对象的观察者(以智能指针的形式存在),当 SimpleQuote
对象通过 setValue
重新设置数值的时候便会通知自己的观察者,由于 Handle
传令官的的角色定位,其他接受 Handle
作为构造函数参数的类紧接着会收到通知(因为这些类在接受 Handle
时也会注册成为 Handle
的观察者),而在这一步,某些计算会被触发。所以,Handle
必须扮演双重角色。
Handle
存在的意义
Quote
本身就能通知自己的观察者,为何还要再经过 Handle
之手?
在 C++ 工程实践中智能指针被广泛应用,函数的接口通常以引用或智能指针的形式存在。如果仅仅传递一个 Quote
的智能指针,当指针在外部重新指向其他地址之后,通知观察者的动作其实是无法被触发的。因此,需要一个机制来管理指针自身的变化,这也就是 Handle
要完成的任务。确切的说,其实是 RelinkedHandle
的任务。这也就是为什么 QuantLib 大部分构造函数被设计成了接受 Handle
对象,而非 Quote
对象或智能指针。
Handle
与 RelinkedHandle
Handle
的架构层次只有两层,派生类 RelinkedHandle
仅扩充了一个接口 linkTo
。RelinkedHandle
通过 linkTo
可以重置自身成为其他对象的观察者。
单纯从代码的角度看,因为架构层次很浅,linkTo
完全有理由放在基类中,以派生类的形式开放此接口可能出于工程稳健性的角度考虑。
作为观察者模式的实现,修改成员变量是一件非常慎重的事,仅在派生类中提供这一功能有两点好处。一方面,整个系统通过派生类拥有了这一功能,可以在必要时启用;另一方面,由于基类中没有这一接口,特殊情况下要启用此功能需要额外编写转化代码,此功能在最大程度上被限制住了,起到了风险隔离的作用。