我正在使用不可变对象构建整个应用程序,以便更容易实现多线程和撤消。我正在使用提供Map,List和Set的不可变版本的Google Collections Library。
我的应用程序模型看起来像一棵树:
对象图可能如下所示:
Scene
|
+-- Node
|
+-- Node
|
+- Port
+-- Node
|
+- Port
+- Port
如果所有这些对象都是不可变的,则由顶级SceneController对象控制:
更一般地说:
答案 0 :(得分:11)
这里有两个感兴趣的概念。首先,持久性数据结构。如果树的所有元素都是不可变的,那么可以通过替换某些部分从原始树派生出一个新树,但是引用较旧的部分,从而节省时间和内存。
例如,如果要向已有两个端口的节点添加第三个端口,则必须创建一个新场景,一个新场景的节点后代以及要更改的节点。另一个节点和所有端口不需要重新创建 - 您只需在新的场景/节点中引用它们。
另一个概念是 Zipper 。拉链是一种“导航”持久数据结构以优化本地更改的方法。例如,如果您添加了四个新端口而不是一个,但是您一次添加一个端口,则必须创建四个新场景和八个新节点。使用拉链,您可以推迟这些创作,直到完成为止,节省了这些中间对象。
我读过有关拉链的最佳解释是here。
现在,使用拉链来导航数据结构,无需使用反向链接。通过巧妙地使用递归构造函数,可以在不可变结构中具有反向链接。但是,这样的数据结构不会是持久。非持久性不可变数据结构具有糟糕的修改性能,因为您每次都需要复制整个数据。
至于学术文献,我推荐Okasaki的纯函数数据结构(dissertation PDF,fully fledged book)。
答案 1 :(得分:3)
如果你的树是不可变的,那么如果你想要改变它,你必须生成一棵新树。
这听起来很糟糕,但如果你的所有节点都是不可变的,那就不是了!由于您不需要复制不可变对象,因此除了您所做的更改之外,新树将主要引用旧树。
您必须以这样的方式设计树,即每个不可变树引用其他不可变树。这样,您就不需要重现整个不可变树。
但是如果你去了不可变的树路线,那么你就不能拥有反向链接。否则你不能重用子树。