原型模式 prototype 创建型 设计模式(七)

时间:2022-12-27 22:52:10
原型模式  prototype

意图

用原型实例指定需要创建的对象的类型,然后使用复制这个原型对象的方法创建出更多同类型的对象
 
显然,原型模式就是给出一个对象,然后克隆一个或者更多个对象
小时候看过的动画片《西游记》,主题曲猴哥中有一句“拔一根毫毛 ,吹出猴万个 ”这就是原型模式
孙悟空作为原型对象,“拔一根毫毛 ,吹” 这就是调用复制对象方法,“猴”万个,就是结果了,创建了“万个” 猴子 

原型模式的根本-拷贝

原型模式的根本在于对象的拷贝
说白了就是:如何复制一个对象?
 
对象的表示
Object ref = new Object();
上面这句话可以理解为三个步骤
  1. 创建一个Object类型的引用名为 ref
  2. 创建了一个Object类型的对象
  3. 使变量ref指向这个新的对象的内存地址
原型模式 prototype 创建型 设计模式(七)
 
一个对象内部有一个或多个属性
属性可能是基本类型也可能是一个引用类型
而对于引用类型就相当于我们上面表述的这种形式
Object ref = new Object();
 
引用变量指向实际的对象
 
深拷贝和浅拷贝
拷贝有两种形式,浅拷贝和深拷贝
浅拷贝被复制的对象所有的属性成员变量都含有与原来的对象相同的值
也就是说如果是引用类型,他仍旧会指向原来的对象,也就是所有的备份中的引用类型指向的是同一个对象
浅拷贝仅仅拷贝当前对象本身
原型模式 prototype 创建型 设计模式(七)
 
深拷贝则恰恰相反,深拷贝将会拷贝所有的对象
也就是如果内部有成员变量为引用类型,那么也会拷贝被指向的对象,不仅仅是拷贝当前对象本身
所有被引用到的对象都复制了一遍
原型模式 prototype 创建型 设计模式(七)
 
对于深拷贝,可以借助于序列化实现,深拷贝一个对象

结构

原型模式作为创建型模式的一种
与工厂模式建造者模式是类似的,都是为了创建对象
只不过是创建的方式不同
原型模式的创建逻辑则是借助于已经存在的对象,调用他的拷贝方法,从而创建相同类型的新的对象
根据依赖倒置原则,面向抽象而不是细节进行编程,所以使用抽象角色Prototype用于描述原型类
一种通用的结构描述形式为:
原型模式 prototype 创建型 设计模式(七)
 
Client 客户端角色
客户端程序发起创建对象请求         
Prototype 抽象原型角色
抽象角色用于描述原型类,给出具体原型类需要的协议 接口或者抽象类
ConcretePrototype具体原型角色
被复制的对象类型

代码示例

package prototype;
public interface Prototype extends Cloneable {
Prototype clone();
}
package prototype;
public class ConcreatePrototype implements Prototype {
@Override
public Prototype clone() {
try{
return (Prototype)super.clone();
}catch (CloneNotSupportedException e){
return null;
}
}
}
package prototype;
public class Client {
public static void main(String[] args){
Prototype prototype = new ConcreatePrototype();
Prototype clonedObj = (Prototype)prototype.clone();
System.out.println(clonedObj.getClass());
System.out.println(prototype == clonedObj);
}
}
原型模式 prototype 创建型 设计模式(七)

Java天然的原型模式

在Java中,所有的对象都继承自Java.lang.Object
Object中有clone()方法 ,可以将一个对象进行拷贝
所以说Java天生的内置了原型模式---通过对象的clone方法进行对象的拷贝
不过也有一些具体的规则需要注意
 
Java语言提供了Cloneable接口,作为标记接口
凡是实现了Cloneable接口的类都声称:“可以安全的在这个类上使用clone()方法”。
试图调用clone()方法时,如果此对象的类没有实现 Cloneable 接口,则会抛出 CloneNotSupportedException
 
clone()方法如下
原型模式 prototype 创建型 设计模式(七)
clone方法是浅拷贝而不是深拷贝
Object中的clone()方法规定了拷贝的一般协议,可以参看API文档  
  1. 对于任何对象 x,表达式:x.clone() != x,克隆对象与原始对象不是同一个对象
  2. x.clone().getClass() == x.getClass() ,克隆对象与原始对象是同一种类型
  3. x.clone().equals(x) 为true
这三点并不是必须的,并不是必须的,并不是必须的,但是除非特殊情况,否则建议应该都满足 
 
Object 类本身不实现接口 Cloneable
所以,如果在类型为Object的对象上调用clone方法,会抛出异常
 
Java语言通过Object提供的protected方法以及Cloneable标记接口机制
定义了一套复制对象的工作流程:
  1. 实现Cloneable接口
  2. 覆盖或者使用继承而来的clone()方法
对于最简单的原型模式 的应用,只需要原型类完成这步即可
这就是Java中原型模式型使用方式
 
因为在Java中,所有的对象直接或者间接地继承Object
所以始终内置的隐含了Object这一抽象角色  
我们的示例代码中,Prototype 就是相当于java中的Object
 
图中Prototype 为具体的原型类(提供了clone方法的类)
原型模式 prototype 创建型 设计模式(七)

拥有管理器的原型模式

原型模式中最为关键的是调用某个对象的拷贝方法,进行原始对象的复制
所以原型模式一种常见的用法就是借助于这个"原始对象",达到工厂方法的效果
客户端中保存一个ConcretePrototype类型的对象
后续的对象创建获取就是客户端通过这个内部的对象,调用它的拷贝方法进行进一步的操作
 
如果产品结构比较简单,可能只需要几种类型的对象即可
上面的原型结构比较适合,客户端自己保存所有的对象
但是
如果产品等级结构比较杂乱,或者说要创建的原型对象是数目并不是固定的
又可以进一步将管理对象的职责进行提取分离,抽象出来一个管理器的角色
专门用于管理这些对象
 
这种带管理器的原型模式中,客户端就不在持有原型对象的引用了,也就是客户端不在拥有原型对象
取而代之的是通过管理器获取
原型模式 prototype 创建型 设计模式(七)
Client 客户端角色
向管理员发起创建对象的请求
Prototype、ConcretePrototype 角色与之前的概念相同
PrototypeManager 角色
原型管理器角色,负责原型对象的创建和管理

示例代码

在原来的基础上增加原型管理器
package prototype;
import java.util.HashMap;
public class PrototypeManager {
/*hashMap维护原型对象
* */
private HashMap<String,Object> map = new HashMap<>();
/*饿汉式单例模式返回创建原型对象管理器
逻辑上原型对象管理器只有一个
* */
private static PrototypeManager prototypeManager= new PrototypeManager();
public static PrototypeManager getPm(){
return prototypeManager;
}
/*初始化内置两个原型对象
* */
private PrototypeManager(){
map.put("product1",new ConcreatePrototype());
map.put("product2",new ConcreatePrototype());
}
/*提供了添加原型对象方法*/
public void add(String key,Prototype prototype){
map.put(key,prototype);
}
/*提供了获取对象的方法,获取的对象依赖的是clone,而不是保存进去的对象*/
public Prototype get(String key){
return ((Prototype)map.get(key)).clone();
}
}
测试类Client角色中也增加相关代码
原型模式 prototype 创建型 设计模式(七)
看得出来,从对象管理器中获取的对象,都是原有对象的一个clone  并不是相同的对象
带管理器的原型模式也叫做 登记形式的原型模式
登记就是相当于在管理器中备案

适用场景

为什么要使用原型模式?
简简单单的new一个对象不好么?为什么非要复制呢?
 
当创建新的对象的过程较为复杂时,使用原型模式可以简化对象的创建过程
比如初始化占用时间较长,这种情况下创建一个对象将不再简单,所以考虑原型模式
 
对于工厂模式中,工厂需要有与产品等级结构相同的结构
一个工厂生产一种产品
然而,如果产品的等级结构容易发生变化的话,工厂类的等级结构可能不得不进行变化
也就是说对于产品等级结构容易变化的场景来说,工厂模式将会不方便
如果采用原型模式,那么就不再需要关注产品的等级结构,产品的等级结构可以随意变动
因为原型模式仅仅关注具体的产品对象,对象之间的结构以及结构的变化并不会产生影响
所以在这种情况下,原型模式拥有比工厂模式更为灵活,扩展性更好
不过
代价是每个类中都必须有一个clone方法,对象的创建逻辑从每个工厂转移到了每个clone方法中
 
在框架中使用原型模式可以与生成的实例进行解耦
框架中面向抽象进行编程,只关注他的抽象类型,不关注他的具体类型
具体的对象可以通过配置文件等方式注入
框架借助于原型模式可以获得这种类型的对象,而完全不用关注这个类型
否则当你使用某种类型时,你必然需要创建对象,也就是始终要接触到具体的类型
这种方法就可以让你永远不知道具体的类型,彻底的分离

总结

原型模式的根本在于复制,所以依赖拷贝方法,java也内置了这种模式
在java中使用时,只需要实现Cloneable接口并且重写或者使用继承而来的clone方法即可
原型模式可以很好地隐藏创建对象的具体类型
当创建一个对象变得复杂时,我们可以考虑使用原型模式 通过复制的方式简化对象的创建过程
但是这有一个前提,那就是复制对象相对比较简单
但是,但是,但是,有的时候,复制一个对象本身却也是非常复杂的,一般可以借助于序列化来进行
而且,每一个类都需要拥有clone方法,当需要对已有的类进行扩展改造时,clone方法也需要进行修改
这并不复合开闭原则
 

原型模式 prototype 创建型 设计模式(七)的更多相关文章

  1. 设计模式05: Prototype 原型模式(创建型模式)

    Prototype 原型模式(创建型模式) 依赖关系的倒置抽象不应该依赖于实现细节,细节应该依赖于抽象.对所有的设计模式都是这样的. -抽象A直接依赖于实现细节b -抽象A依赖于抽象B,实现细节b依赖 ...

  2. Java设计模式05:常用设计模式之原型模式(创建型模式)

    1. Java之原型模式(Prototype Pattern)     原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象. ...

  3. 跟着实例学习设计模式(7)-原型模式prototype(创建型)

    原型模式是创建型模式. 设计意图:用原型实例指定创建对象的类型,并通过拷贝这个原型来创建新的对象. 我们使用构建简历的样例的类图来说明原型模式. 类图: 原型模式主要用于对象的复制.它的核心是就是类图 ...

  4. 谈谈设计模式~原型模式&lpar;Prototype&rpar;

    返回目录 原型模式是创建型模式的一种,其特点在于通过“复制”一个已经存在的实例来返回新的实例(clone),而不是新建(new)实例.被复制的实例就是我们所称的“原型”,这个原型是可定制的. 原型模式 ...

  5. 原型模式--prototype

    C++设计模式——原型模式 什么是原型模式? 在GOF的<设计模式:可复用面向对象软件的基础>中是这样说的:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.这这个定义中,最 ...

  6. 设计模式(四)原型模式Prototype(创建型)

      设计模式(四)原型模式Prototype(创建型) 1.   概述 我们都知道,创建型模式一般是用来创建一个新的对象,然后我们使用这个对象完成一些对象的操作,我们通过原型模式可以快速的创建一个对象 ...

  7. 设计模式学习之原型模式(Prototype&comma;创建型模式)(5)

    通过序列化的方式实现深拷贝 [Serializable] public class Person:ICloneable { public string Name { get; set; } publi ...

  8. Prototype&comma;创建型模式

    读书笔记_探索式测试_混合探索式测试   一.测试场景 1.讲述用户故事 2.描述需求 3.演示产品功能 4.演示集成场景 5.描述设置和安装 6.描述警告和出错情况 二.使用基于场景的探索式测试 1 ...

  9. OOAD-设计模式(三)之创建型设计模式(5种)

    前言 前面介绍了OOAD的基础知识,现在我们来详细的说明一下GOF设计模式中的23种模式,希望大家能够学到东西! 一.工厂方法模式(Factory Method) 1.1.工厂方法模式概述 工厂方法模 ...

随机推荐

  1. Win10全屏看视频时任务栏不隐藏

    解决办法: 1.对任务栏鼠标右键点击,选择"任务管理器" 2.在进程选项下找到Windows进程中的Windows资源管理器 3.对Windows资源管理器鼠标右键,选择重新启动

  2. Linux LVM硬盘管理及LVM扩容

    LVM磁盘管理 一.LVM简介... 1 二. LVM基本术语... 2 三. 安装LVM... 3 四. 创建和管理LVM... 4 2. 创建PV.. 6 3. 创建VG.. 7 4. 创建LV. ...

  3. EXCEL某列长度超过255个字符导入SQL SERVER的处理方法

    问题描述: [Excel 源 [1]] 错误: 输出“Excel 源输出”(9) 上的 输出列“Description 3”(546) 出错.返回的列状态是:“文本被截断,或者一个或多个字符在目标代码 ...

  4. PHP array&lowbar;chunk&lpar;&rpar; 函数

    今天在CSDN上,看到了一个问题 一维数组 PHP code   array('0'=>'a',1=>'b',2=>'c',3=>'d',4=>'e',5=>'f' ...

  5. vue防止闪烁

    v-text也可以 转意的话使用v-html <style> [v-clock]{ display:none } <style> <span>{{msg}}< ...

  6. for语句嵌套使用 实现9&ast;9乘法表

         这个实例主要考察对for循环语句的使用,出现递增规律的乘法表. 开发环境      开发工具:Microsoft Visual Studio2010 旗舰版 具体步骤      先是制作一个 ...

  7. 球谐光照(Spherical Harmonics Lighting)及其应用-应用篇

    上一篇介绍了球谐函数的一些原理和性质,本篇主要介绍如何实现球谐光照,将这种光照应用到实际的场景中去. 我们知道,球谐光照实际上就是将周围的环境光采样成几个系数,然后渲染的时候用这几个系数来对光照进行还 ...

  8. 微信企业号C&num;开发配置API

    微信开发第一步 :   配置API,实现接收消息服务配置 1.在下图界面先填好内容,事件消息处理可自行选择,我这里是没处理的 2.第二步: 使用vs 进行代码的编写,以下是我的代码.LogTextHe ...

  9. 多行文字水平垂直居中在div

    <BODY>   <div class="box">    <h3>1.单行文字居中</h3>    <!--设置行高来实现- ...

  10. python处理Windows平台上路径有空格

    最近在采集windows上中间件的时候,遇到了文件路径有空格的问题. 例如:Aapche的安装路径为D:\Program Files\Apache Software Foundation\Apache ...