[学习笔记]设计模式之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模式).这是她还曾身为女巫时留下的 ...
随机推荐
- hdu 3062
2-SAT的入门题: 网上说这个算法最好的入门教材是:伍昱的<由对称性解2-SAT问题>的ppt和赵爽的论文<2-SAT 解法浅析>: 看了一下伍昱的ppt,很好理解! 而这道 ...
- 关于 OneAPM Cloud Test DNS 监控的几个重要问题
你注意到了吗?OneAPM Cloud Test 已经全面开启支持 DNS 监控了! CT 产品自上线以来一直致力于产品完善,希望能够尽可能全面地满足用户需求,为您提供完美的用户体验.目前 Cloud ...
- [wikioi]回家
http://wikioi.com/problem/1079/ 单源最短路径,可以用dijkstra来做.这里采用了heap优化,复杂度是(V+E)logV.这里用了STL的优先队列(堆),重复加入p ...
- perl use base 继承
centos6.5:/root/podinns/lib#cat First.pm package First; use base qw(Second); sub new { my $self = {} ...
- pcDuino汉化方法
1,打开终端:2,在终端输入命令 sudo apt-get update 更新一下软件源3, 输入命令下载中文支持包 sudo apt-get install language-pack-gnome- ...
- SIFT算法:KeyPoint找寻、定位与优化
SIFT算法:DoG尺度空间生产 SIFT算法:KeyPoint找寻.定位与优化 SIFT算法:确定特征点方向 SIFT算法:特征描述子 目录: 1.找寻 2.定位 3.优化 1 KeyPoint ...
- Ruby入门教程和技巧
转自:http://blog.csdn.net/cqfz123/article/details/1349050 Ruby真的比Java更好? Ruby On Rails 创始人:对Java 说再见 ...
- 动态规划(DP计数):HDU 5121 Just A Mistake
As we all know, Matt is an outstanding contestant in ACM-ICPC. Graph problems are his favorite.Once, ...
- Oracle Statspack报告中各项指标含义详解~~学习性能必看!!!
Oracle Statspack报告中各项指标含义详解~~学习性能必看!!! Data Buffer Hit Ratio#<#90# 数据块在数据缓冲区中的命中率,通常应该在90%以上,否则考虑 ...
- vijosP1388 二叉树数
vijosP1388 二叉树数 链接:https://vijos.org/p/1388 [思路] Catalan数.根据公式h=C(2n,n)/(n+1)计算.首先化简为 (n+i)/i的积(1< ...