设计模式笔记(十九)– 原型模式

我们已学完了三类设计模式类型中的两类:结构类型、行为类型,这一篇开始学习最后一类:创建类型。创建类型的设计模式都是关于创建对象的。

首先是原型模式(Prototype Pattern),当我们通过复制已存在的对象(原型)来创建新对象时,可以使用该模式。


问题提出

来看实际场景。

打开Mac系统的Keynote应用,它类似于Windows下的PowerPoint,在画板里,选择并绘制某个形状比如圆形,然后右键点击该圆形弹出菜单,选择“复制”,将生成一个同样的圆形。

我们用代码来实现这个场景。

创建接口Component,定义render方法。我们命名为Component而不是Shape主要考虑到复制的对象不一定是圆形、正方形等形状,还有可能复制文本、图片等对象。

创建Circle类实现Component接口,Circle有一个私有属性radius及其getter和setter。

创建ContextMenu类,代表右键菜单。它有duplicate方法,用于复制选择的对象并绘制到画板(文档)。在该方法里,首先需要检查选择的对象是Circle,然后复制并创建一个新的Circle对象,再进行绘制。

形状有很多,比如正方形、三角形,我们要多次判断对象的具体类型,然后才创建。

这里就有一些问题。

首先,违反开放闭合原则,该原则要求我们对扩展开放而对修改闭合,这里每添加一种新形状我们必须回来修改这个duplicate方法。

其次,ContextMenu和Circle是耦合的,也即ContextMenu与所有的形状都是耦合的。

其三,必须了解所有的形状,无法支持其他开发者编写的扩展形状。


解决方案

当前结构是这样的。ContextMenu负责实现复制功能,它依赖于具体的形状Circle。

首先,我们将复制的职责移至Circle,Circle了解复制它自己所需的所有信息,比如半径、颜色等。我们给它添加方法clone。

显然,clone并不限于Circle,其他形状都需要。我们将其添加到Component接口。这样ContextMenu的依赖就可以变成Component接口而非具体的形状Circle。

这个就叫做原型模式(Prototype Pattern)。

四人帮的经典图如下。

原型用Prototype命名,相当于我们的Component。ConcretePrototype相当于Circle,而Client则相当于我们的ContextMenu。


解决方案

修改Component接口,定义新方法clone,它返回Component。

修改Circle类,实现clone方法。原先在ContextMenu的duplicate方法里需要判断Component的类型,在clone里不再需要了。

其他的形状不管是正方形还是三角形,复制对象的实现也都在内部。

接着修改ContextMenu,我们不再需要判断类型,只需简单地调用Component的clone方法即可。

ContextMenu无需了解Circle,它只需与Component接口对话。添加新的形状不会影响现有的代码,符合开放闭合的原则。


小结

复制已存在的对象(原型)来创建新对象,通常可以使用原型模式(Prototype Pattern)。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注