[学习笔记]设计模式之Prototype
写在前面
为方便读者,本文已添加至索引:
设计模式
学习笔记索引
在笔记Builder模式中,我们曾见到了最初用于创建平行世界的函数createWorld,并且它是Mage类的成员函数(毕竟是专属于魔导士的强大咒语嘛)。然而在上篇笔记Singleton模式中,时の魔导士组建了一个极为强大的WorldMgr议会来代替他维持世界。“如果他们甚至连改造地形的能力都没有的话,会让人很苦恼呢……”魔导士心想,“或许我可以给他们提供一套地图编辑器……或者说世界改造器,就像暴雪那帮家伙的星际争霸。”对于如何设计一套通用的世界改造器,时の魔导士打算引入Prototype模式。它能极大地减少系统中类的数目,同时也更易于在其中添加新的环境因素。
要点梳理
- 目的分类
- 对象创建型模式
- 范围准则
- 对象(该模式处理对象间的关系,这些关系在运行时刻是可以变化的,更具动态性)
- 主要功能
- 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
- 适用情况
- 当一个系统应该独立于它的产品创建、构成和表示时
- 当要实例化的类是在运行时刻指定时,例如,通过动态装载
- 为了避免创建一个与产品类层次平行的工厂类层次时
- 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
- 参与部分
- Prototype:声明一个克隆自身的接口
- ConcretePrototype:实现一个克隆自身的操作
- Client:让一个原型克隆自身从而创建一个新的对象
- 协作过程
- 客户请求一个原型克隆自身
- UML图

示例分析 - 世界改造工具套装内部测试版
首先,我们提供WorldPrototypeEditor类,它将使用要创建的对象的原型来初始化,这样我们就不需要仅仅为了改变它所创建的地形的类而再生成子类了:
class WorldPrototypeEditor {
public:
WorldPrototypeEditor(Mountain*, Ocean*, Plant*);
virtual Mountain* makeMountain();
virtual Ocean* makeOcean();
virtual Plant* makePlant(Mountain*);
private:
Mountain* _prototypeMountain;
Ocean* _prototypeOcean;
Plant* _prototypePlant;
}
由于是内部测试版,我们仅仅考虑最简单的地形改造器啦,能造山,造水,然后在山上种些小树苗什么的就完事了。它的的构造器只初始化它的原型:
WorldPrototypeEditor::WorldPrototypeEditor(Mountain* m, Ocean* o, Plant* p)
{
_prototypeMountain = m;
_prototypeOcean = o;
_prototypePlant = p;
}
用于创建山、海洋和树的成员函数是相似的:每个都要克隆一个原型,然后初始化。让我们来看看makeMountain和makeTree的定义:
Mountain* WorldPrototypeEditor::makeMountain()
{
return _prototypeMountain->clone();
} Plant* WorldPrototypeEditor::makePlant(Mountain* m)
{
Plant* p = _prototypePlant->clone();
p->addTo(m);
return p;
}
因此,当泰坦们开始着手编辑世界地形的时候,只需使用基本地形构件的原型进行初始化,就可以由WorldPrototypeEditor来创建一个原型的或缺省的世界:
WorldPrototypeEditor simpleWorldEditor;
World* world = WorldMgr::getInstance()->editWorld(simpleWorldEditor);
一个可以被用作原型的对象,例如Plant的实例,必须支持clone操作。它还必须有一个拷贝构造器用于克隆。它可能还需要一个独立的操作来重新初始化内部的状态:
class Plant {
public:
Plant();
Plant(const Plant&);
virtual void initialize(Category*);
virtual Plant* clone() const;
private:
Category* _cg;
}
Plant::Plant(const Plant& other) {
_cg = other._cg;
}
void Plant::initialize(Category* c)
{
_cg = c;
}
Plant* Plant::clone() const {
return new Plant(*this);
}
例子中私有成员变量_cg仅仅是决定了植物的种类,但是如果我们想为这个编辑器添加一些扩展包。比如说,增加一些特别的植物:苹果树怎么样?AppleTree必须重定义clone,并且实现相应的构造器。
class AppleTree : public Plant {
public:
AppleTree();
AppleTree(const AppleTree&);
virtual Plant* clone() const;
int hasApple();
private:
int _apple;
}
AppleTree::AppleTree(const AppleTree& other) : Plant(other) {
_apple = other._apple;
}
Plant* AppleTree::clone() const {
return new AppleTree(*this);
}
在这个情况下,我们可以用带苹果树扩展包的原型集合来初始化WorldPrototypeEditor,见下面的调用:
WorldPrototypeEditor appleWorldEditor(new Mountain(), new Ocean(), new AppleTree());
我们可以看一个简单的UML图来更加直观地感受:

特点总结
Prototype模式有许多和Abstract Factory、Builder模式一样的效果:它对客户隐藏了具体的产品类,因此减少了客户需要知道的名字的数目。此外,这些模式使客户无需改变即可使用与特定应用相关的类。当然Prototype模式还有它独到的一面:
- 运行时刻增加和删除产品。Prototype允许只通过客户注册原型实例就可以将一个新的具体产品类并入系统。它比其他创建型模式更为灵活,因为客户可以在运行时刻建立和删除原型。
- 改变值以指定新对象。高度动态的系统允许我们通过对象复合定义新的行为。例如,通过为一个对象变量指定值,并且不定义新的类。我们通过实例化已有类并且将这些实例注册为客户对象的原型,就可以有效定义新类别的对象。客户可以将职责代理给原型,从而表现出新的行为。
- 改变结构以指定新对象。许多应用由部件和子部件来创建对象。例如电路设计编辑器就是由子电路来构造电路的。为方便起见,这样的应用通常允许我们实例化复杂的、用户定义的结构,比方说,一次又一次的重复使用一个特定的子电路。对于Prototype模式,我们仅需将这个子电路作为一个原型增加到可用的电路元素选择板中。
- 减少子类的构造。Factory Method经常产生一个与产品类层次平行的Creator类层次。Prototype模式使得我们克隆一个原型而不是请求一个工厂方法去产生一个新的对象,因此我们根本不需要Creator类层次。这一优点主要适用于像C++这样不将类作为一级类对象的语言。
- 用类动态配置。应用一些运行时刻环境允许我们动态将类装载到应用中。
对于有些语言,例如JavaScript,它就提供了一个等价于原型的东西(对象),原型模式几乎是它所固有的特性,无处不在。
写在最后
今天的笔记就到这里了,欢迎大家批评指正!如果觉得可以的话,好文推荐一下,我会非常感谢的!
[学习笔记]设计模式之Prototype的更多相关文章
- Java程序猿的JavaScript学习笔记(5——prototype和Object内置方法)
计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript ...
- [学习笔记]设计模式之Abstract Factory
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 在上篇笔记Builder设计模式中,时の魔导士祭出了自己的WorldCreator.尽管它因此能创造出一个有山有树有房子的世界,但是白 ...
- [学习笔记]设计模式之Builder
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 作为一个新入职的魔导士呢,哦不,是程序员,我以为并没有太多机会去设计项目的软件架构.但是,工作一段时间之后,自己渐渐意识到,哪怕是自己 ...
- [学习笔记]设计模式之Adapter
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 Adapter(适配器)模式主要解决接口不匹配的问题.为此,让我们要回到最初Builder模式创建平行世界时,白雪公主和小霍比特人的谜 ...
- [学习笔记]设计模式之Bridge
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 “魔镜啊魔镜,谁是这个世界上最美丽的人?”月光中,一个低沉的声音回荡在女王的卧室.“是美丽的白雪公主,她正和小霍比特人们幸福快乐地生活 ...
- [学习笔记]设计模式之Command
为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 在上篇Chain of Responsibility(职责链)模式笔记中,我们学习了一种行为型设计模式.今天,我们继续这一主题,来学习 ...
- [学习笔记]设计模式之Chain of Responsibility
为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 最近时间比较紧,所以发文的速度相对较慢了.但是看到园子里有很多朋友对设计模式感兴趣,我感觉很高兴,能够和大家一起学习这些知识. 之前的 ...
- [学习笔记]设计模式之Composite
为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 在Composite(组合)模式中,用户可以使用多个简单的组件以形成较大的组件,而这些组件还可能进一步组合成更大的.它重要的特性是能够 ...
- [学习笔记]设计模式之Proxy
为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 “魔镜啊魔镜,谁是这个世界上最美丽的人?” 每到晚上,女王都会问魔镜相同的问题(见Decorator模式).这是她还曾身为女巫时留下的 ...
随机推荐
- Linux 下编译、安装、配置 QT
转自Linux 下编译.安装.配置 QT 注意:编译安装耗时费力,且很容易出错,要不断调整编译参数,不推荐使用,否则这将会是一个纠结痛苦的过程. 打算做嵌入式图像处理,计划方案嵌入式Linux+Ope ...
- JavaScript 将字符串转化为json对象
var json = eval('(' + data + ')'); 其中data为字符串数据
- linux网络配置正确,能够ping通内网地址,无法打开外网网页
在虚拟机里面装了linux后,发现内网能访问,外网访问不了. 首先确定网络配置没有问题,并且能够访问外网,通过以下方法进行确认: [root@localhost ~]# more /etc/sysco ...
- 【POJ 1984】Navigation Nightmare(带权并查集)
Navigation Nightmare Description Farmer John's pastoral neighborhood has N farms (2 <= N <= 40 ...
- FFmpeg发送流媒体的命令(UDP,RTP,RTMP)
http://blog.csdn.net/leixiaohua1020/article/details/38283297
- Xamarin IOS – hello word
原文:Xamarin IOS – hello word 环境 硬件:Macbook pro Retina 13 系统:10.11.3 EI Capitan Xcode:7.0 Ps:配置刚刚够用. 安 ...
- No modifications are allowed to a locked ParameterMap
错误:java.lang.IllegalStateException: No modifications are allowed to a locked ParameterMap at org.apa ...
- JAVA 内存泄漏与内存溢出
一.Java内存回收机制 不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址.Java中对象是采用new或者反射或者clone或者反序列化的方法创建的, 这 ...
- C# 如何为应用程序加入多个图标?
对于WINDOWS XP操作系统,浏览文件时有列表,图标和平铺三种,显示出文件图标的大小分别为16x16,32x32,48x48这三种尺寸.有些程序包含这三个尺寸的图标,随着浏览文件时的设置来选择尺寸 ...
- bzoj2553
似乎挂精度了,不过这是一道好题 很明显看题知算法,知道这道题肯定是AC自动机上矩阵乘法 首先要明确一点,对一个字符串,怎样划分禁忌串最多 根据求最多不相交线段可知,从头到尾能划分出禁忌串就划分 根据这 ...