Aha!设计模式(27)-原型(1)

2019-04-13 13:57发布

意图   用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。   虽然看起来是一件事,但却包含了两个方面:一个是被创建对象种类的表达方式的问题;另一个是创建对象过程的问题。这里的对象种类是一个比较微妙的表达方式,它不一定是类的概念,也有可能只是数据,结构的不同。这里先记下一笔。   动机   你可以通过定制一个通用的图形编辑器框架和增加一些表示音符、休止符和五线谱的新对象来构造一个乐谱编辑器。这个编辑器框架可能有一个工具选择板用于将这些音乐对象加到乐谱中。这个选择板可能还包括选择、移动和其他操纵音乐对象的工具。用户可以点击四分音符工具并使用它将四分音符加到乐谱中。或者他们可以使用移动工具在五线谱上上下移动一个音符,从而改变它的音调。   这部分介绍了乐谱编辑器的功能。这个编辑器可以分解为两大块:一块是包含工具选择面板的通用的图形编辑架构,另一块是乐谱,音符,休止符等乐谱编辑特有的部分。   我们假定该框架为音符和五线谱这样的图形构件提供了一个抽象的Graphics类。此外,为定义选择板中的那些工具,还提供一个抽象类Tool。该框架还为一些创建图形对象实例并将它们加入到文档中的工具预定义了一个GraphicTool子类。   这里的GraphicTool对应的是工具选择面板上用来添加图形要素的那些按钮的功能。用户每按下一个GraphcTool对象,就会在编辑区增加一个图形要素。   但GraphicTool给框架设计者带来一个问题。音符和五线谱的类特定于我们的应用,而GraphicTool类却属于框架。GraphicTool不知道如何创建我们的音乐类的实例,并将它们添加到乐谱中。我们可以为每一种音乐对象创建一个GraphicTool的子类,但这样会产生大量的子类,这些子类仅仅在它们所初始化的音乐对象的类别上有所不同。我们知道对象复合是比创建子类更灵活的一种选择。问题是,该框架怎么样用它来参数化GraphicTool的实例,而这些实例是由Graphic类所支持创建的。   任何一个框架都要和实际的应用场景结合起来。对于图形编辑框架来讲,它需要一个办法将GraphicTool和乐谱,音符,休止符等要素联系起来。一个可选的方案就是为每种音乐要素实现一个GraphicTool的子类。这种方式的缺点很明显:子类太多。另外的一个办法是通过对象复合的方式,即通过不同对象之间的组合来实现不同的功能。当然使用这种方式的前提是:构建GraphicTool实例时,有办法通过参数生成不同的组合对象。   解决办法是让GraphicTool通过拷贝或者“克隆”一个Graphic子类的实例来创建新的Graphic,我们称这个实例为一个原型。GraphicTool将它应该克隆和添加到文档中的原型作为参数。如果所有Graphic子类都支持一个Clone操作,那么GraphicTool可以克隆所有种类的Graphic,如下页上图所示。     这种方式的一个特点就是让Graphic子类自己支持Clone操作。这种方式使得GraphicTool可以在不了解子类具体信息的情况下通过一个对象生成该类的另外一个对象。这种方式可以为图形编辑器提供极大的扩展性。   此在我们的音乐编辑器中,用于创建个音乐对象的每一种工具都是一个用不同原型进行初始化的GraphicTool实例。通过克隆一个音乐对象的原型并将这个克隆添加到乐谱中,每个GraphicTool实例都会产生一个音乐对象。   我们甚至可以进一步使用Prototype模式来减少类的数目。我们使用不同的类来表示全音符和半音符,但可能不需要这么做。它们可以是使用不同位图和时延初始化的相同的类的实例。一个创建全音符的工具就是这样的GraphicTool,它的原型是一个被初始化成全音符的MusicalNote。这可以极大的减少系统中类的数目,同时也更易于在音乐编辑器中增加新的音符。   虽然在本例种使用不同的原型(Staff,Who二Note,HalfNode等)构建GraphicTool,但是这并不是必须的。如果我们有办法将各个音乐对象的区别看作是数据(也可以说结构)的不同,就可以参数化这些不同点从而进一步减少系统中子类的数目。   作者观点   除了原型模式本身的说明以外,本文还提到了两种减少子类数目的方法:对象组合和参数化不同点。这为我们设计类结构是提供了另外的视角。    注:   本文中蓝 {MOD}粗体文字都引自《设计模式》一书。   觉得本文有帮助?请分享给更多人。 阅读更多更新文章,请扫描下面二维码,关注微信公众号【面向对象思考】