面向对象设计模式纵横谈:Prototype 原型模式(笔记记录)
有一段时间没写东西了,今天继续把没写完的设计模式写完,今天这堂课是创建型设计模式的最后一堂课,原型设计模式,它同样也是解决了对象在创建的过程中的解耦合的情况,面对变化使代码更稳定,更准确的说是使创建对象的主业务逻辑更稳定。好了,我们继续。我们县讨论一下依赖关系。
依赖关系的倒置
抽象不应该依赖于实现细节,实现细节应该依赖于抽象。
-抽象A直接依赖于实现细节b,实现细节b就类似我要吃米饭,上午可能要吃米饭,下午就可能要吃炸酱面,每个人习惯不同,这个是经常变化的。对于软件设计来说,我们可能设计一个我要吃饭的接口,具体吃什么,是米饭还是炸酱面,通过其他方法实现,这样就保证了软件系统接口的稳定性。抽象A和实现细节b的关系就像【人-----》吃米饭】,这样的关系不好,软件易变化,就是容易改代码。这样的关系应该改成【人------》吃饭】,整个环境就安静了,稳定了。
-抽象A依赖于抽象B,实现细节b依赖于抽象B,这幅图就是【人-----》吃饭】,具体吃什么饭以后再说。
动机(Motivation)
在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。
如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?
意图(Intent)
使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象——《设计模式》GoF
这是原话,很精炼,我们看看类图吧,更明确一点。
结构(Structure)
我们看了这么多设计模式的类图了,大家应该也总结出一些经验来,客户端依赖的都是抽象的接口,【这个接口不是C#语言里面的Interface或者抽象类】,原型模式里面有一个抽象原型对象,他是稳定的,通过自身的克隆实现对象的创建。
例说Prototype应用,代码截图如下:
上面的代码,GameSystem依赖于具体的new的对象,如果面临对象变化,例如如果要增加一个新的角色,就要重新修改编译了。对此,我们就可以用工厂方法来改变,但是,对于每一个类型都要写一个工厂类,比较繁琐。我们也可以用抽象工厂方法,创建一组对象,如果这样做,这个实现有点笨重,如果增加一个角色面临抽象工厂的剧烈变化。所以我们这里选择使用原型模式。
先把GameSystem里用到的类型换为抽象类型,这些抽象的类型是面向客户端的,或者说是客户使用的接口。
再将需要new的具体对象用参数传入,这样在GameSystem这个客户程序里面就只依赖于抽象而不依赖于具体了。具体的NormalActorA、FlyActorA等都不出现在GameSystem中。
应用程序
给抽象类增加Clone抽象方法
给具体类实现Clone方法
但有一点要注意,MemberwiseClone方法只是一种浅拷贝,它只能拷贝所有的值类型和String,如果是引用类型(例如数组),它就会只拷贝引用,而不会重新创建对象,例如对数组,就只会拷贝数组的地址。
如下图,左边是栈,右边是堆,(.NET的类都是在堆上)
如果想深拷贝,除了用笨办法,还可以用序列化的方式来做。首先需要把类标记为可序列化,然后将类序列化到内存,再把内存中的类反序列化,反序列化得到的对象和原来的对象一定是深拷贝。
在对于结构中的图,可以对应为:Client就是GameSystem,Operation方法就是Run。Prototype抽象类就对应NormalActor,ConcretePrototype即NormalActorA。
Prototype模式的几个要点
Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有“稳定的接口”。
Prototype模式对于“如何创建易变类的实体对象”(创建型模式除了Singleton模式以外,都是用于解决创建易变类的实体对象的问题的)采用“原型克隆”的方法来做,它使得我们可以非常灵活地动态创建“拥有某些稳定接口”的新对象——所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方不断地Clone。
Prototype模式中的Clone方法可以利用.NET中的Object类的MemberwiseClone()方法或者序列化来实现深拷贝。
有关创建型模式的讨论
Singleton模式解决的是实体对象个数的问题。除了Singleton之外,其他创建型模式解决的都是new所带来的耦合关系。
Factory Method,Abstract Factory,Builder都需要一个额外的工厂类来负责实例化“易变对象”,而Prototype则是通过原型(一个特殊的工厂类)来克隆“易变对象”。(其实原型就是一个特殊的工厂类,它只是把工厂和实体对象耦合在一起了)
如果遇到“易变类”,起初的设计通常从Factory Method开始,当遇到更多的复杂变化时,再考虑重构为其他三种工厂模式(Abstract Factory,Builder,Prototype)。
一般来说,如果可以使用Factory Method,那么一定可以使用Prototype。但是Prototype的使用情况一般是在类比较容易克隆的条件之上,如果是每个类实现比较简单,都可以只用实现MemberwiseClone,没有引用类型的深拷贝,那么就更适合了。Prototype如果要实现深拷贝,还需要在每个要克隆的类上加序列化标签,这点复杂度要考虑进程序中。
面向对象设计模式纵横谈:Prototype 原型模式(笔记记录)的更多相关文章
- 设计模式(1)--Prototype(原型模式)--创建型
1.模式定义: 原型模式就是用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象. 2.使用场景: 在原型模式中我们可以利用过一个原型对象来指明我们所要创建对象的类型,然后通过复制这个对象的 ...
- Java设计模式:Prototype(原型)模式
概念定义 使用原型实例指定待创建对象的种类,并通过拷贝该原型来创建新的对象.Prototype模式允许一个原型对象克隆(复制)出多个与其相同的对象,而无需知道任何如何创建的细节. 应用场景 对象的创建 ...
- 面向对象设计模式纵横谈:Singelton单件模式(笔记记录)
李建忠老师讲的<面向对象设计模式纵横谈>,早就看过了,现在有了时间重新整理一下,以前的博客[赛迪网]没有了,现在搬到博客园,重新过一遍,也便于以后浏览. 设计模式从不同的角度分类会得 ...
- 设计模式:Prototype 原型模式 - 同学你抄过别人的作业么?-clone()方法的使用
原型模式: 通过某个类的实例来创建对象 使用原型模式的好处: 好处是什么呢?当我们需要多次重复的创建一个类的示例的时候,我们可以使用new但是,new不仅仅耗费内存而且,如果new 某个类的构造方法中 ...
- 设计模式05: Prototype 原型模式(创建型模式)
Prototype 原型模式(创建型模式) 依赖关系的倒置抽象不应该依赖于实现细节,细节应该依赖于抽象.对所有的设计模式都是这样的. -抽象A直接依赖于实现细节b -抽象A依赖于抽象B,实现细节b依赖 ...
- 一天一个设计模式——Prototype 原型模式
一.模式说明 看了比较多的资料,对原型模式写的比较复杂,个人的理解就是模型复制,根据现有的类来直接创建新的类,而不是调用类的构造函数. 那为什么不直接调用new方法来创建类的实例呢,主要一个原因是如果 ...
- Prototype原型模式(创建型模式)
1.原型模式解决的问题 现在有一个抽象的游戏设施建造系统,负责构建一个现代风格和古典风格的房屋和道路. 前提:抽象变化较慢,实现变化较快(不稳定) 整个抽象的游戏设施建造系统相对变化较慢,本例中只有一 ...
- C#面向对象设计模式纵横谈——1.面向对象设计模式与原则
一:设计模式简介 每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心. ---- Christopher Alexander 软件设计领域设计模式: 设计模式描述了软件设计过 ...
- 设计模式之GOF23原型模式01
原型模式prototype 原型模式: - 通过new产生一个对象需要非常繁琐的数据准备或者访问权限,则可以使用原型模式,比如如果new对象所需时间过长,可以通过克隆产生相同的副本 - Java中的克 ...
- 7、Prototype 原型模式 通过复制创造实例 创造型模式
2020-07-19 发哥讲 发哥讲 其实上一节的末尾讲到如何去生成对象,其中有一个关于clone的,这其实就是Prototype原型模式. 通过克隆(拷贝)的方式生成对象 1.了解Prototype ...
随机推荐
- 网卡虚拟化技术:VMDq和SR-IOV
通常情况下,一个服务器上跑几十个虚机,对计算和网络的需求是很惊人的.前者促生了当下的多核技术发展,后者则不能简单的用多网卡来实现.试想,每个虚机如果都需要10G的交换能力,服务器要配置几十块物理网卡, ...
- selenium+python自动化92-多线程启动多个不同浏览器
前言 如果想用多个浏览器跑同一套测试代码,driver=webdriver.Firefox()这里的driver就不能写死了,可以把浏览器名称参数化. 后续如果想实现多线程同时启动浏览器执行用例,用前 ...
- selenium+python自动化84-chrome手机wap模式(登录淘宝页面)
前言 chrome手机wap模式登录淘宝页面,点击验证码无效问题解决. 切换到wap模式,使用TouchActions模块用tap方法触摸 我的环境 chrome 62 chromedriver 2. ...
- C++官方文档-常量成员函数
#include <iostream> using namespace std; class MyClass { public: int x; static int n; const in ...
- CUDA C Programming Guide 在线教程学习笔记 Part 10【坑】
▶ 动态并行. ● 动态并行直接从 GPU 上创建工作,可以减少主机和设备间数据传输,在设备线程中调整配置.有数据依赖的并行工作可以在内核运行时生成,并利用 GPU 的硬件调度和负载均衡.动态并行要求 ...
- HTML5 位运算符
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- UI5-文档-4.16-Dialogs and Fragments
在这一步中,我们将进一步研究另一个可以用来组装视图的元素:the fragment. 片段是轻量级UI部件(UI子树),可以重用,但是没有任何控制器.这意味着,每当你想定义一个特定UI的一部分是跨多个 ...
- OpenCv 人脸识别 基础
#include <opencv2\opencv.hpp> #include <iostream> using namespace std; int main() { // 摄 ...
- pod优先级与抢占测试
# kubectl describe node k8s-n2Name: k8s-n2Roles: <none>Labels: ...
- Hadoop 集群的一些问题
1.创建用户hadoop adduser hadoop passwd hadoop usermod -a -G hadoop hadoop chown -R hadoop:hadoop /data ...