设计模式之禅8
来源:互联网 发布:linux curl post body 编辑:程序博客网 时间:2024/06/06 17:17
设计模式之禅8
真刀实枪之原型模式
- 先发个个性化电子账单来引出今天的主角
- 个性化服务
- 一般银行都会要求个性化服务,添加一些个人信息在提示信息之前。
- 递送的成功率
- 邮件的递送成功率有一定的要求,由于大批的发送邮件,会被接收的邮件服务器认为是垃圾邮件,因此要在头信息增加一些伪数据,以规避被反垃圾邮件引擎认为是垃圾邮件
- 个性化服务
- 电子账单系统
- 账单分析
- 账单生成器
- 广告信息管理
- 发送队列管理
- 发送机
- 退信处理
- 报表管理
类图
- 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方法 @Override protected 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 block e.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 block e.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 block e.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方法 @Override protected 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("我是构造函数!"); } @Override protected 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>(); @Override protected 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>(); @Override protected 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>(); @Override protected 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版;
- 仅供学习,严禁商业用途;
- 代码手写,没有经编译器编译,有个别错误,自行根据上下文改正。
阅读全文
0 0
- 设计模式之禅8
- 设计模式之禅
- 设计模式之禅
- 设计模式之禅
- 设计模式之禅
- 设计模式之禅
- 设计模式之禅
- 设计模式之禅
- 设计模式之禅笔记8--建造者模式
- [设计模式]<<设计模式之禅>>模板方法模式
- 《设计模式之禅》目录
- 《设计模式之禅》前言
- 《设计模式之禅》书评
- 《设计模式之禅》书评
- 初读《设计模式之禅》
- 《设计模式之禅》书评
- 《设计模式之禅》摘记
- 设计模式之禅读书笔记
- linux-lvm基本应用
- 3.2 计算浮点数相除的余数
- Leetcode 322 & 518
- 微机原理输入字符串并比较
- [FireBug Lite 集成 Cent Browser Java开发]
- 设计模式之禅8
- Skynet基础入门例子详解(2)
- 微机原理成绩分级
- 3.1 计算并联电阻的阻值
- java日常学习:TreeSet
- 微机原理银行系统
- CodeForces 888C Dominant Character
- oracle SQL语句取本周本月本年的数据
- Contruct2游戏进阶教程