Duplicate Observed Data 复制被监视的数据
你有一些领域数据置身于GUI控件中,而领域函数需要访问这些数据。将该数据复制到一个领域对象中。建立一个Observer模式,用以同步领域对象和GUI对象内的重复数据。
动机
一个分层良好的系统,应该将处理用户界面和处理业务逻辑的代码分开。之所以这样做,原因有以下几点:(1)你可能需要使用不同的用户界面来表现相同的业务逻辑,如果同时承担两种责任,用户界面会变得过分复杂;(2)与GUI隔离之后,领域对象的维护和演化都会更容易,你甚至可以让不同的开发者负责不同部分的开发。
尽管吋以轻松地将“行为”划分到不同部位,“数据”却往往不能如此。同一项数据有可能既需要内嵌于GUI控件,也需要保存于领域模型里。自从MVC(Model-View-Controller,模型-视图-控制器)模式出现后,用户界面框架都使用多层系统来提供某种机制,使你不但可以提供这类数据,并保持它们同步。
如果你遇到的代码是以两层方式开发,业务逻辑被内嵌于用户界面之中,你就有必要将行为分离出来。其中的主要工作就是函数的分解和搬移。但数据就不同了:你不能仅仅只是移动数据,必须将它复制到新的对象中,并提供相应的同步机制。
做法
- 修改展现类,使其成为领域类的Observer[GoF]。
- 如果尚未有领域类,就建立一个。
- 如果没有“从展现类到领域类”的关联,就将领域类保存于展现类的一个字段中。
- 针对GUI类中的领域数据,使用Self Encapsulate Field。
- 编译,测试。
- 在事件处理函数中调用设值函数,直接更新GUI组件。
- 在事件处理函数中放一个设值函数,利用它将GUI组件更新为领域数据的当前值。当然这其实没有必要,你只不过是拿它的值设定它自己。但是这样使用设值函数,便是允许其中的任何动作得以于日后被执行起来,这是 这一步骤的意义所在。
- 进行这个改变时,对于组件,不要使用取值函数,应该直接取用,因为稍后我们将修改取值函数,使其从领域对象(而非GUI组件)取值。设值函数也将做类似修改。
- 确保测试代码能够触发新添加的事件处理机制。
- 编译,测试。
- 在领域类中定义数据及其相关访问函数。
- 确保领域类中的设值函数能够触发Observer模式的通报机制。
- 对于被观察的数据,在领域类中使用与展现类所用的相同类型(通常是字符串)来保存。后续重构中你可以自由改变这个数据类型。
- 修改展现类中的访问函数,将它们的操作对象改为领域对象(而非GUI组件)。
- 修改Observer的update(),使其从相应的领域对象中将所需数据复制给GUI组件。
- 编译,测试。