3.2 Immutable Object模式的架构
Immutable Object模式将现实世界中状态可变的实体建模为状态不可变的对象,并通过创建不同的状态不可变的对象来反映实现世界实体的状态变更。
Immutable Object模式的主要参与者有以下几种,其类图如图3-1所示。
图3-1 Immutable Object模式的类图
• ImmutableObject:负责存储一组不可变状态。该参与者不对外暴露任何可以修改其状态的方法,其主要方法及职责如下。
➢ getStateX、getStateN:这些getter方法返回其所属ImmutableObject实例维护的状态相关变量的值。这些变量在对象实例化时通过其构造器的参数获得值。
➢ getStateSnapshot:返回其所属ImmutableObject实例维护的一组状态的快照。
• Manipulator:负责维护ImmutableObject所建模的现实世界实体状态的变更。当相应的现实世界实体状态变更时,该参与者负责生成新的ImmutableObject实例,以反映新的状态。
➢ changeStateTo:根据新的状态值生成新的ImmutableObject实例。
状态不可变对象的使用情况主要包括以下几种类型。
• 获取单个状态的值:调用不可变对象的相关getter方法即可实现。
• 获取一组状态的快照:状态不可变对象可以提供一个getter方法,该方法需要对其返回值做防御性复制(Defensive Copy)或者返回一个只读对象,以避免因其状态对外泄露而被改变。
• 生成新的状态不可变对象实例:当被建模对象的状态发生变化的时候,创建新的状态不可变对象实例来反映这种变化。
Immutable Object模式的典型交互场景的序列图如图3-2所示。
图3-2 Immutable Object模式的典型交互场景的序列图
第1~4步:客户端代码获取当前ImmutableObject实例的各个状态值。
第5步:客户端代码调用Manipulator的changeStateTo方法来更新应用的状态。
第6、7步:changeStateTo方法创建新的ImmutableObject实例,以反映应用的新状态,并返回。
第8、9步:客户端代码获取新的ImmutableObject实例的状态快照。
一个严格意义上的状态不可变对象需要满足以下所有条件。
1. 类本身使用final修饰:防止其子类改变其定义的行为。
2. 所有字段都是用final修饰的:使用final修饰不仅仅是从语义上说明被修饰字段的引用不可改变,更重要的是这个语义在多线程环境下由JMM(Java Memory Model)保证了被修饰字段所引用对象的初始化安全,即final修饰的字段在其他线程可见时,它必定已完成初始化。相反,非final修饰的字段由于缺少这种保证,可能导致在一个线程“看到”一个字段的时候,它还未完成初始化,从而可能导致一些不可预料的结果。
3. 在创建对象的过程中没有泄露this关键字给其他类:防止其他类(如该类的内部匿名类)在对象创建过程中修改其状态。
4. 若任何字段引用了其他状态可变的对象(如集合、数组等),则这些字段必须是由private关键字修饰的,并且这些字段的值不能对外暴露。若有相关方法要返回这些字段的值,应该进行防御性复制。