原汁原味设计模式-原型prototype

来源:互联网 发布:android源码里编译apk 编辑:程序博客网 时间:2024/05/22 04:43

原创文章,转载请标明来源。

原型模式不等于克隆

背景介绍

原型模式是被大多数人忽略的一个模式,一提到原型模式,大多数人的概念就是clone方法,其实这是不对的。clone只是原型模式实现过程中的一小部分,更重要的是原型模式的设计思想,以及应用场景。

当前的设计模式书籍中,更多的强调了clone,强调了浅拷贝和深拷贝的区别,以及clone带来的性能优势,其实这个方向是错误的。原型模式其实是很有趣、很实用的一个模式,下面我带大家来领略一下原型模式的魅力。

GOF的原文中举的例子是这样的:设计一个乐谱编辑器,乐谱编辑器有很多面板,如音符面板,和休止符面板。我这里找了一个小巧的乐谱编辑器,并打开了两个面板:音符和标记,如下:

用户可以从面板上选择一个组件,然后把它拖到左边的五线谱上。

我们假设这些组件的基类叫Graphic,它有两个子类:MusicalNote和Symbol,分别代表音符和标记。

我们的framework需要支持不同的组件,那么就面临一个问题:如何生成这些组件?

最简单的一个方式,是根据点击的面板来创建不同的对象:


switch(pos){
case 1:
p = new HalfNote();//半音符,Musical的子类
break;
case 2:
p = new **Symbol();
break;
default:
break;
}

以上这段可以封装为简单工厂。
或者采用标准的工厂模式,提供GraphicTool类,由不同的子类来生成不同的对象:

上面的方式会带来两个问题:
1. 类膨胀,每一个Graphic的子类都要有一个GraphicTool的子类。
2. 强相关性。考虑如果Graphic不是由framework来实现,而是交给外部实现,那么framework根本不知道有哪些具体实现类,也就没办法来实例化了。

使用原型模式来解决

为了解决上面的两个问题,我们可以使用原型模式。
我们把Graphic**及它的子类**的实例称为原型。
在面板初始化好后,就直接生成全系列的实例,即生成全部原型。
当用户点击面板的时候,调用原型的clone方法,来生成一个新实例,用这个新实例来进行绘制。

伪码如下:
p = getCurPrototype(pos);//根据位置拿到原型
np = p.clone();//克隆一个新实例
np.draw();

为什么要clone

看到这里可能会有个疑惑,我们为什么要克隆一个新实例出来呢,直接使用现有的实例不可以吗?
答案是我们会对这个实例进行调整,比如设置音符的音高。如果是改面板的实例,就会带来全局的影响。因此克隆一个新实例的做法更加恰当。

0 0
原创粉丝点击