设计模式之禅【原型模式】
真刀实枪之原型模式
- 先发个个性化电子账单来引出今天的主角
- 个性化服务
- 一般银行都会要求个性化服务,添加一些个人信息在提示信息之前。
- 递送的成功率
- 邮件的递送成功率有一定的要求,由于大批的发送邮件,会被接收的邮件服务器认为是垃圾邮件,因此要在头信息增加一些伪数据,以规避被反垃圾邮件引擎认为是垃圾邮件
- 个性化服务
- 电子账单系统
- 账单分析
- 账单生成器
- 广告信息管理
- 发送队列管理
- 发送机
- 退信处理
- 报表管理
类图
- 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;
- 类图很简单,核心就是一个clone方法,看看Cloneable接口,一个方法也没有,那么clone方法从哪里来?别忘了超类Object,其中有方法为clone
通用源码(核心代码)
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版;
- 仅供学习,严禁商业用途;
- 代码手写,没有经编译器编译,有个别错误,自行根据上下文改正。
设计模式之禅【原型模式】相关推荐
- Java描述设计模式(05):原型模式
一.原型模式简介 1.基础概念 原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象. 2.模式结构 原型模式要求对象实现一个 ...
- 设计模式学习笔记-原型模式
一.概述 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象: 二.模式中的角色 Prototype:声明一个克隆自身的接口: ConcretePrototype:实现一个克隆自身的操作: ...
- 【设计模式 06】原型模式(克隆??)
原型模式(clone?) Prototype pattern refers to creating duplicate object while keeping performance in mind ...
- java 设计模式 优缺点_java设计模式2:原型模式(机制\优缺点分析\使用场景)...
1. 原型模式实现机制 原型模式在设计模式中相对比较简单,它直接通过实现 Cloneable接口,再重写 clone()方法返回想要的对象就OK 了. 一起来看下代码 : public class P ...
- Java设计模式5:原型模式
原型模式 原型模式属于对象的创建模式,通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的用意. 原型模式结构 原型模式要求对象实现一个 ...
- 设计模式之【原型模式】,深入理解深拷贝与浅拷贝
文章目录 一.什么是原型模式 二.原型模式实现方式 1.传统方式 2.原型模式 熟悉浅拷贝和深拷贝 浅拷贝实现对象克隆 深拷贝实现对象克隆 一.什么是原型模式 原型模式: 用一个已经创建的实例作为原型 ...
- 【设计模式自习室】原型模式
前言 <设计模式自习室>系列,顾名思义,本系列文章带你温习常见的设计模式.主要内容有: 该设计模式的详细介绍,包括: 引子,意图(大白话解释) 类图,时序图(理论规范) 该模式的代码示例: ...
- java设计模式之五(原型模式)
什么是原型模式? 原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 当我们程序中有几个相似但又不 ...
- 设计模式 之美 -- 原型模式
文章目录 1. 解决问题 2. 应用场景 3. 实现方式 C++实现 C语言实现 4. 缺点 5. 和其他三种创建模式的对比(单例,工厂,建造者) 1. 解决问题 如果对象的创建成本较大,而同一个类的 ...
- 设计模式入门之原型模式Prototype
//原型模式:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象 //简单来说,当进行面向接口编程时,假设须要复制这一接口对象时.因为不知道他的详细类型并且不能实例化一个接口 //这时就须要 ...
最新文章
- 修改表结构添加外键约束,默认外键名
- 下单问题分析及解决方式
- 【Pytorch神经网络实战案例】01 CIFAR-10数据集:Pytorch使用GPU训练CNN模版-方法①
- windows下端口占用解决方法-查看和杀死占用端口进程
- 爬虫中 Selenium-Requets-模拟登陆cookie-代理proxy 的简单总结
- LOJ 2085: 洛谷 P1587: bzoj 4652: 「NOI2016」循环之美
- matplotlib 中文_看了这个总结,其实 Matplotlib 可视化,也没那么难
- Linux内存管理学习资料
- windows下mysql备份
- SpringMVC的RESTful(二)定制格式
- Android OpenGLES2.0使用
- LayoutInflater类的作用与用法
- 50年的追踪拍摄:社会阶级能被逾越吗?
- html 心形字符,心形符号大全
- 免费查询IP的API
- MySQL中B+树索引,聚簇索引,二级索引,辅助索引,回表,索引生效条件
- wkhtmltox 中文显示一半_免费!联合国官员孩子上的中文课,这次我get到了~
- 你所需要的java基础提升篇大总结
- 多边形交叉区域计算面积_使用GPC或Clipper计算多边形交叉区域
- 计算机常用英语关键词意思,英语写作中常用关键词和短语汇总
热门文章
- vue 点击当前路由怎么重新加载_Vue 路由切换时页面内容没有重新加载的解决方法...
- Debug Diagnostic Tool
- linux中pwd命令,pwd命令
- html 纯css设置转圈,CSS3 转圈彩色文字动画实例及animation-play-state属性规则
- redis常用命令总结,一文足以(5种基本数据结构+bitmap+Geo+HyperLogLog+Streams)
- (二十一)资产(组合)的预期收益率和风险
- 初中三年级数学可以用计算机吗,不到3分钟,这份初中数学攻略被家长和学生疯狂转发!太实用了!...
- QQ隐藏图原理与C#实现(含源文件)
- mysql函数ceil和ceiling
- SpringBoot application.properties读取属性配置文件中文显示为乱码问题的解决