真刀实枪之原型模式

  • 先发个个性化电子账单来引出今天的主角

    • 个性化服务

      • 一般银行都会要求个性化服务,添加一些个人信息在提示信息之前。
    • 递送的成功率
      • 邮件的递送成功率有一定的要求,由于大批的发送邮件,会被接收的邮件服务器认为是垃圾邮件,因此要在头信息增加一些伪数据,以规避被反垃圾邮件引擎认为是垃圾邮件
  • 电子账单系统
    1. 账单分析
    2. 账单生成器
    3. 广告信息管理
    4. 发送队列管理
    5. 发送机
    6. 退信处理
    7. 报表管理
  • 类图

    • AdvTemplate是广告模板
    • Mail是一封邮件类
    • 代码

      • AdvTemplate

        package com.peng.mail;/*** @author kungfu~peng* @data 2017年11月19日* @description*/
        public class AdvTemplate {// 广告信名称private String advSubject = "kungfu银行抽奖活动";// 广告信内容private String advContext = "抽奖活动,只要抽中就给你100W";// 获得广告的名称public String getAdvSubject() {return advSubject;}// 获得广告的内容public String getAdvContext() {return advContext;}}
        
      • Mail

        package com.peng.mail;/*** @author kungfu~peng* @data 2017年11月19日* @description*/
        public class Mail {// 收件人private String receiver;// 邮件名称private String subject;// 称谓private String application;// 邮件内容private String context;// 邮件尾部private String tail;// 构造函数public Mail(AdvTemplate advTemplate) {this.subject = advTemplate.getAdvSubject();this.context = advTemplate.getAdvContext();}public String getReceiver() {return receiver;}public void setReceiver(String receiver) {this.receiver = receiver;}public String getSubject() {return subject;}public void setSubject(String subject) {this.subject = subject;}public String getApplication() {return application;}public void setApplication(String application) {this.application = application;}public String getContext() {return context;}public void setContext(String context) {this.context = context;}public String getTail() {return tail;}public void setTail(String tail) {this.tail = tail;}}
        
      • Client

        package com.peng.mail;import java.util.Random;/*** @author kungfu~peng* @data 2017年11月19日* @description*/
        public class Client {// 发送账单的数量private static int MAX_COUNT = 10;public static void main(String[] args) {// 模拟发送邮int i = 0;// 把模板定义出来Mail mail = new Mail(new AdvTemplate());mail.setTail("版权归kungfu银行所有。");while (i < MAX_COUNT) {// 发往的地方mail.setApplication(getRandString(5) + "女士/先生");// 发送邮件mail.setReceiver(getRandString(5) + "@" + getRandString(8) + ".com");sendMail(mail);i++;}}// 函数--发送邮件public static void sendMail(Mail mail) {System.out.println("主题:" + mail.getSubject() + ",收件人:"+ mail.getReceiver() + ",发送成功!");}// 获得指定长度的随机字符串public static String getRandString(int maxLength) {String source = "abcdefghijklmnopqrstABCDEFGHIJKLMNOPQRST";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < maxLength; i++) {sb.append(source.charAt(random.nextInt(source.length())));}return sb.toString();}
        }
        
    • 这个程序有问题吗?单线程没啥大问题,可能时间长点,但是试过多线程没!!出现了线程的混乱,出现了重复发送。既然出现了问题,那么先来改改我们的类图吧!

      • 代码

        • Mail

          package com.peng.mail;/*** @author kungfu~peng* @data 2017年11月19日* @description*/
          public class Mail implements Cloneable {// 收件人private String receiver;// 邮件名称private String subject;// 称谓private String application;// 邮件内容private String context;// 邮件尾部private String tail;// 构造函数public Mail(AdvTemplate advTemplate) {this.subject = advTemplate.getAdvSubject();this.context = advTemplate.getAdvContext();}// clone方法@Overrideprotected Object clone() throws CloneNotSupportedException {Mail mail = null;try {mail = (Mail) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return mail;}public String getReceiver() {return receiver;}public void setReceiver(String receiver) {this.receiver = receiver;}public String getSubject() {return subject;}public void setSubject(String subject) {this.subject = subject;}public String getApplication() {return application;}public void setApplication(String application) {this.application = application;}public String getContext() {return context;}public void setContext(String context) {this.context = context;}public String getTail() {return tail;}public void setTail(String tail) {this.tail = tail;}}
          
        • 修改后的Client

          package com.peng.mail;import java.util.Random;/*** @author kungfu~peng* @data 2017年11月19日* @description*/
          public class Client {// 发送账单的数量private static int MAX_COUNT = 10;public static void main(String[] args) throws CloneNotSupportedException {new Thread() {public void run() {// 模拟发送邮int i = 0;// 把模板定义出来Mail mail = new Mail(new AdvTemplate());mail.setTail("版权归kungfu银行所有。");while (i < MAX_COUNT) {Mail cloneObj = null;try {cloneObj = mail.clone();} catch (CloneNotSupportedException e) {// TODO Auto-generated catch blocke.printStackTrace();}// 发往的地方cloneObj.setApplication(getRandString(5) + "女士/先生");// 发送邮件cloneObj.setReceiver(getRandString(5) + "@"+ getRandString(8) + ".com");sendMail(cloneObj);i++;}};}.start();new Thread() {public void run() {// 模拟发送邮int i = 0;// 把模板定义出来Mail mail = new Mail(new AdvTemplate());mail.setTail("版权归kungfu银行所有。");while (i < MAX_COUNT) {Mail cloneObj = null;try {cloneObj = mail.clone();} catch (CloneNotSupportedException e) {// TODO Auto-generated catch blocke.printStackTrace();}// 发往的地方cloneObj.setApplication(getRandString(5) + "女士/先生");// 发送邮件cloneObj.setReceiver(getRandString(5) + "@"+ getRandString(8) + ".com");sendMail(cloneObj);i++;}};}.start();new Thread() {public void run() {// 模拟发送邮int i = 0;// 把模板定义出来Mail mail = new Mail(new AdvTemplate());mail.setTail("版权归kungfu银行所有。");while (i < MAX_COUNT) {Mail cloneObj = null;try {cloneObj = mail.clone();} catch (CloneNotSupportedException e) {// TODO Auto-generated catch blocke.printStackTrace();}// 发往的地方cloneObj.setApplication(getRandString(5) + "女士/先生");// 发送邮件cloneObj.setReceiver(getRandString(5) + "@"+ getRandString(8) + ".com");sendMail(cloneObj);i++;}};}.start();}// 函数--发送邮件public static void sendMail(Mail mail) {System.out.println("主题:" + mail.getSubject() + ",收件人:"+ mail.getReceiver() + ",发送成功!");}// 获得指定长度的随机字符串public static String getRandString(int maxLength) {String source = "abcdefghijklmnopqrstABCDEFGHIJKLMNOPQRST";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < maxLength; i++) {sb.append(source.charAt(random.nextInt(source.length())));}return sb.toString();}
          }
          
        • 运行结果(随机数据各有千秋)
          主题:kungfu银行抽奖活动,收件人:aHiKE@dMcLCdAk.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:DqTKg@fCNnqLJm.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:jreLC@BBPnJOlD.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:tkfgH@hScMgggf.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:TKbpF@HcbObcBh.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:MbgIb@DAtBSSPc.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:mPGGE@TFJCPONn.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:TcHSS@cHQLcSTF.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:IrDMO@bPceTKBc.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:HCiAg@krTkPHRM.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:psJHk@kKQkddCP.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:cMlrQ@sPmRLjmP.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:HjatC@HgCrjjtn.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:FQnJG@ARPRcLAj.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:QBmEN@BDosMQpS.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:PsDmL@CnOmCCEl.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:iMNlo@SGqkStEK.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:GBbHP@sdrjeOjE.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:GPsSf@QNpaQFKD.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:HbHKl@IjrtpNBR.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:THhCQ@GmfGTfqT.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:GnbIs@FEnFSrTL.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:EpggK@boqLdbJf.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:Jblbp@GKnCDRsD.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:fiCOd@sKqinsQT.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:pBEmd@GJJNeIoH.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:Ftdmp@qropOmna.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:tSnBP@mlNGChpn.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:jgbja@POTIiPBG.com,发送成功!
          主题:kungfu银行抽奖活动,收件人:nahpO@kLqHMaDL.com,发送成功!
          
        • 上边代码中,把对象复制一份,产生一个新的对象,和原有对象一致,然后修改细节数据。这种不通过new关键字来产生的一个对象,而是通过对象的复制来实现的模式成为原型模式

原型模式的定义

  • Prototype Pattern
  • Specify the kinds of objects to create using a prototypical instance , and create new objects by copying this prototype.(用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象)
  • 通用类图
    • 类图很简单,核心就是一个clone方法,看看Cloneable接口,一个方法也没有,那么clone方法从哪里来?别忘了超类Object,其中有方法为clone
      • Object类中的clone方法原码: protected native Object clone() throws CloneNotSupportedException;
  • 通用源码(核心代码)

    • Prototype

      package com.peng.yuanxing;/*** @author kungfu~peng* @data 2017年11月19日* @description*/
      public class Prototype implements Cloneable {// 覆写父类的clone方法@Overrideprotected Prototype clone() throws CloneNotSupportedException {Prototype prototype = null;try {prototype = (Prototype) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return prototype;}
      }
      

原型模式的应用

  • 原型模式的优点

    • 性能优良:内存二进制流的拷贝
    • 逃避构造函数的约束:直接在内存中执行,构造函数不会执行
  • 使用场景
    • 资源优化:数据、硬件资源
    • 性能和安全的场景
    • 一个对象多个修改者的场景
  • 在实际项目中,原型模式很少单独出现,一般是和工厂模式一起出现,通过clone方法创建一个对象,然后由工厂方法提供给调用者。

原型模式的注意事项

  • 构造方法不被执行(如下:)

    package com.peng.yuanxing;/*** @author kungfu~peng* @data 2017年11月19日* @description*/
    public class Thing implements Cloneable {public Thing() {super();System.out.println("我是构造函数!");}@Overrideprotected Thing clone() throws CloneNotSupportedException {Thing thing = null;try {thing = (Thing) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return thing;}public static void main(String[] args) throws CloneNotSupportedException {Thing t0 = new Thing();Thing t1 = t0.clone();Thing t2 = t0.clone();Thing t3 = t0.clone();}
    }
    
    • 执行结果

      我是构造函数!
      
    • 解释:Object的clone方法的原理是从内存中(堆内存)以二进制流的方式进行拷贝,重新分配了一块内存块,那构造函数没有被执行也是非常正常的了

浅拷贝

  • 浅拷贝:Java偷懒的拷贝动作,Object类提供的方法clone只是拷贝对象,其对象的数组,引用对象等都不拷贝,还是指向原生对象的内部元素地址。两个对象共享一个私有变量,你改我改大家都改,是一种不安全的方式【拷贝的有:原始类型比如int,long,char等,String就把它当成基本类型--它的处理机制比较麻烦一点(它没有clone方法,通过字符串池stringcool在需要时才在内存中创建新的字符串)】
  • 使用原型模型时,引用的成员变量必须满足两个条件才不被拷贝

    1. 类的成员变量,而不是方法内变量
    2. 必须是一个可变对象的引用对象,而不是一个原始类型和一个不变对象
    
    • 例子

      package com.peng.yuanxing;import java.util.ArrayList;/*** @author kungfu~peng* @data 2017年11月19日* @description*/
      public class Thing implements Cloneable {private ArrayList<String> list = new ArrayList<String>();@Overrideprotected Thing clone() throws CloneNotSupportedException {Thing thing = null;try {thing = (Thing) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return thing;}// 添加值public void setValue(String value) {list.add(value);}// 获取数据public ArrayList<String> getValues() {return this.list;}public static void main(String[] args) throws CloneNotSupportedException {// 创建对象Thing t0 = new Thing();t0.setValue("1");t0.setValue("2");t0.setValue("3");// 克隆对象Thing coleThing = t0.clone();coleThing.setValue("a");coleThing.setValue("b");coleThing.setValue("c");// 打印数据System.out.println(t0.getValues());System.out.println(coleThing.getValues());}
      }
      
    • 执行结果
      [1, 2, 3, a, b, c]
      [1, 2, 3, a, b, c]
      

深拷贝

  • 深拷贝:

    • 例子

      package com.peng.yuanxing;import java.util.ArrayList;/*** @author kungfu~peng* @data 2017年11月19日* @description*/
      public class Thing implements Cloneable {private ArrayList<String> list = new ArrayList<String>();@Overrideprotected Thing clone() throws CloneNotSupportedException {Thing thing = null;try {thing = (Thing) super.clone();thing.list = (ArrayList<String>) this.list.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return thing;}// 添加值public void setValue(String value) {list.add(value);}// 获取数据public ArrayList<String> getValues() {return this.list;}public static void main(String[] args) throws CloneNotSupportedException {// 创建对象Thing t0 = new Thing();t0.setValue("1");t0.setValue("2");t0.setValue("3");// 克隆对象Thing coleThing = t0.clone();coleThing.setValue("a");coleThing.setValue("b");coleThing.setValue("c");// 打印数据System.out.println(t0.getValues());System.out.println(coleThing.getValues());}
      }
      
    • 执行结果

      [1, 2, 3]
      [1, 2, 3, a, b, c]
      

注意

  • 深拷贝和浅拷贝建议不要混合使用,特别是在涉及类的继承时,父类有多个引用的情况就非常复杂,建议的方案是深拷贝和浅拷贝分开实现

clone与final(用冤家来记)

  • 例:增加final修饰,编译报错

    package com.peng.yuanxing;import java.util.ArrayList;/*** @author kungfu~peng* @data 2017年11月19日* @description*/
    public class Thing implements Cloneable {private final ArrayList<String> list = new ArrayList<String>();@Overrideprotected Thing clone() throws CloneNotSupportedException {Thing thing = null;try {thing = (Thing) super.clone();thing.list = (ArrayList<String>) this.list.clone();//编译报错:The final field Thing.list cannot be assigned} catch (CloneNotSupportedException e) {e.printStackTrace();}return thing;}// 添加值public void setValue(String value) {list.add(value);}// 获取数据public ArrayList<String> getValues() {return this.list;}
    }
    
  • java中,final修饰的不可以被修改,引用数据类型则为地址不可变,list的地址改变了,自然就报错

最佳实践

  • 原型模型产生出大量信息相同的类,然后可以拷出副本,然后进行修改信息
  • 一个对象的起步可以不用从零起步,直接从一个已经具有雏形的对象克隆,然后再修改为生产的对象

声明

  • 摘自秦小波《设计模式之禅》第2版;
  • 仅供学习,严禁商业用途;
  • 代码手写,没有经编译器编译,有个别错误,自行根据上下文改正。

设计模式之禅【原型模式】相关推荐

  1. Java描述设计模式(05):原型模式

    一.原型模式简介 1.基础概念 原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象. 2.模式结构 原型模式要求对象实现一个 ...

  2. 设计模式学习笔记-原型模式

    一.概述 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象: 二.模式中的角色 Prototype:声明一个克隆自身的接口: ConcretePrototype:实现一个克隆自身的操作: ...

  3. 【设计模式 06】原型模式(克隆??)

    原型模式(clone?) Prototype pattern refers to creating duplicate object while keeping performance in mind ...

  4. java 设计模式 优缺点_java设计模式2:原型模式(机制\优缺点分析\使用场景)...

    1. 原型模式实现机制 原型模式在设计模式中相对比较简单,它直接通过实现 Cloneable接口,再重写 clone()方法返回想要的对象就OK 了. 一起来看下代码 : public class P ...

  5. Java设计模式5:原型模式

    原型模式 原型模式属于对象的创建模式,通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的用意. 原型模式结构 原型模式要求对象实现一个 ...

  6. 设计模式之【原型模式】,深入理解深拷贝与浅拷贝

    文章目录 一.什么是原型模式 二.原型模式实现方式 1.传统方式 2.原型模式 熟悉浅拷贝和深拷贝 浅拷贝实现对象克隆 深拷贝实现对象克隆 一.什么是原型模式 原型模式: 用一个已经创建的实例作为原型 ...

  7. 【设计模式自习室】原型模式

    前言 <设计模式自习室>系列,顾名思义,本系列文章带你温习常见的设计模式.主要内容有: 该设计模式的详细介绍,包括: 引子,意图(大白话解释) 类图,时序图(理论规范) 该模式的代码示例: ...

  8. java设计模式之五(原型模式)

    什么是原型模式? 原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 当我们程序中有几个相似但又不 ...

  9. 设计模式 之美 -- 原型模式

    文章目录 1. 解决问题 2. 应用场景 3. 实现方式 C++实现 C语言实现 4. 缺点 5. 和其他三种创建模式的对比(单例,工厂,建造者) 1. 解决问题 如果对象的创建成本较大,而同一个类的 ...

  10. 设计模式入门之原型模式Prototype

    //原型模式:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象 //简单来说,当进行面向接口编程时,假设须要复制这一接口对象时.因为不知道他的详细类型并且不能实例化一个接口 //这时就须要 ...

最新文章

  1. 修改表结构添加外键约束,默认外键名
  2. 下单问题分析及解决方式
  3. 【Pytorch神经网络实战案例】01 CIFAR-10数据集:Pytorch使用GPU训练CNN模版-方法①
  4. windows下端口占用解决方法-查看和杀死占用端口进程
  5. 爬虫中 Selenium-Requets-模拟登陆cookie-代理proxy 的简单总结
  6. LOJ 2085: 洛谷 P1587: bzoj 4652: 「NOI2016」循环之美
  7. matplotlib 中文_看了这个总结,其实 Matplotlib 可视化,也没那么难
  8. Linux内存管理学习资料
  9. windows下mysql备份
  10. SpringMVC的RESTful(二)定制格式
  11. Android OpenGLES2.0使用
  12. LayoutInflater类的作用与用法
  13. 50年的追踪拍摄:社会阶级能被逾越吗?
  14. html 心形字符,心形符号大全
  15. 免费查询IP的API
  16. MySQL中B+树索引,聚簇索引,二级索引,辅助索引,回表,索引生效条件
  17. wkhtmltox 中文显示一半_免费!联合国官员孩子上的中文课,这次我get到了~
  18. 你所需要的java基础提升篇大总结
  19. 多边形交叉区域计算面积_使用GPC或Clipper计算多边形交叉区域
  20. 计算机常用英语关键词意思,英语写作中常用关键词和短语汇总

热门文章

  1. vue 点击当前路由怎么重新加载_Vue 路由切换时页面内容没有重新加载的解决方法...
  2. Debug Diagnostic Tool
  3. linux中pwd命令,pwd命令
  4. html 纯css设置转圈,CSS3 转圈彩色文字动画实例及animation-play-state属性规则
  5. redis常用命令总结,一文足以(5种基本数据结构+bitmap+Geo+HyperLogLog+Streams)
  6. (二十一)资产(组合)的预期收益率和风险
  7. 初中三年级数学可以用计算机吗,不到3分钟,这份初中数学攻略被家长和学生疯狂转发!太实用了!...
  8. QQ隐藏图原理与C#实现(含源文件)
  9. mysql函数ceil和ceiling
  10. SpringBoot application.properties读取属性配置文件中文显示为乱码问题的解决