懒初始化 与 可见状态


懒初始化

原文:LazyInitialization    设计            Bliki 索引

一个变量(在OO语境中常为class的一个字段)第一次存取时才被初始化,这项技术叫做懒初始化,其常规形式如下(C#):

public FooClass Foo {
  get {
    if (_foo = null) _foo = calculateFoo();
    return _foo;
  }
}

若得到某个字段值的计算过程很耗时,你想推迟至真正用到它时再计算,这时适宜采用懒初始化。也就是说,这个字段很多情况下用不到,或我们为了迅速初始化这个object宁愿把耗时的事往后拖,懒初始化通常很适合处理这些情况。

需要记住这是一项优化技术,一些值客户端当即并不需要,它们被懒初始化,这种优化提高了响应性。与其他优化技术一样,应到确实需要解决性能问题时再采用。

尤其是这项技术还可能导致调试上的麻烦。比如在调试过程中,你查看一个懒初始化字段,它被赋值的那一刻也就意味着系统发生了一个状态改变(尽管可见状态没变,但实际状态变了),而这在正常情况下(不需此字段时)并不发生,因此当你加上个调试打印语句时,bug们好像无影无踪,一直藏到周五下午才发难——记住我传授你的这个捉虫秘方噢


可见状态

原文:ObservableState    设计            Bliki 索引

人们说一个方法不修改一个object的可见状态,这是什么意思呢?

把修改状态的方法和不修改的分开,这么做很有用。不修改状态的方法(我称之为query)可以在任何上下文中调用,无需像别的方法那样担心它们的调用顺序。

这里的关键点并不是它们什么状态都不修改,而是不修改可见状态。一个object的可见状态是那些通过query方法可以得到的状态——还是看一个快捷的例子吧。

最简单例子是缓存(cache),看下边这个表示区间的class:

# ruby
class MyRange
    attr_reader :start, :finish
    def initialize start, finish
        @start, @finish = start, finish
        @lengthCache = nil
    end
    def length
        @lengthCache = (@finish - @start) unless @lengthCache
        return @lengthCache
    end
end

我们有一个变量lengthCache,应用懒初始化技术,第一访问时被填充。给lengthCache赋值显然对这个object的实际状态做了修改,但它的可见状态并没改变,因为你从外面看不到这个object的状态变了。

我说“从外面看不到”意思是,不管lengthCache的值已经被填充了还是没被填充,这个MyRange object的任何方法被别的object调用所得到的结果都一样。

实现MyRange其他方法时,所有用到区间长度的地方都不直接读lengthCache字段,而是调用length方法获得——这是达到上述效果的典型做法。缓存的改变不应该是可见的,这是个明显的例子。另外一点是,所有懒初始化过程都不应修改可见状态。

没有更多推荐了,返回首页