设计模式之禅之设计模式-原型模式

一:原型模式的定义
        --->用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
        --->原型模式(Prototype Pattern)的简单程度仅次于单例模式和迭代器模式。正是由于简单,使用的场景才非常地多
        --->原型模式的核心是一个clone方法,通过该方法进行对象的拷贝,Java提供了一个Cloneable接口来标示这个对象是可拷贝的,为什么说是“标示”呢?翻开JDK的帮助看看Cloneable是一个方法都没有的,这个接口只是一个标记作用,在JVM中具有这个标记的对象才有可能被拷贝。那怎么才能从“有可能被拷贝”转换为“可以被拷贝”呢?方法是覆盖clone()方法,是的,你没有看错是重写clone()方法,看看我们上面Mail类中的clone方法.该方法重写了Object对象的方法

二:原型模式的优点

        ● 性能优良
               ---> 原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。

        ● 逃避构造函数的约束
               --->这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的(参见13.4节)。优点就是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。

三:原型模式的应用场景

        ● 资源优化场景
                --->类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
        ● 性能和安全要求的场景
                --->通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
        ● 一个对象多个修改者的场景
                --->一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
                --->在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与Java融为一体,大家可以随手拿来使用。

四:原型模式的注意事项
        ---> 构造函数不会被执行
        --->注意浅拷贝。你可能会比较奇怪,为什么在Mail那个类中就可以使用String类型,而不会产生由浅拷贝带来的问题呢?内部的数组和引用对象才不拷贝,其他的原始类型比如int、long、char等都会被拷贝,但是对于String类型,Java就希望你把它认为是基本类型,它是没有clone方法的,处理机制也比较特殊,通过字符串池(stringpool)在需要的时候才在内存中创建新的字符串,读者在使用的时候就把String当做基本类使用即可。
        --->使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原始类型或不可变对象。
        --->深拷贝还有一种实现方式就是通过自己写二进制流来操作对象,然后实现对象的深拷贝,这个大家有时间自己实现一下
        --->深拷贝和浅拷贝建议不要混合使用,特别是在涉及类的继承时,父类有多个引用的情况就非常复杂,建议的方案是深拷贝和浅拷贝分开实现。

五:clone和fianl两个冤家
        --->你要实现深拷贝的梦想在final关键字的威胁下破灭了,路总是有的,我们来想想怎么修改这个方法:删除掉final关键字,这是最便捷、安全、快速的方式
        --->你要使用clone方法,在类的成员变量上就不要增加final关键字。

六:原型模式最佳实践

        --->可以这样理解:一个对象的产生可以不由零起步,直接从一个已经具备一定雏形的对象克隆,然后再修改为生产需要的对象。也就是说,产生一个人,可以不从1岁长到2岁,再到3岁......也可以直接找一个人,从其身上获得DNA,然后克隆一个,直接修改一下就是30岁了!我们讲的原型模式也就是这样的功能

七:原型模式例子

【1】原型模式的模板

 1 package com.yeepay.sxf.template8;
 2 /**
 3  * 邮件
 4  * @author sxf
 5  *
 6  * 原型模式:(1)实现Cloneable接口
 7  *                    (2)重写Object的clone方法
 8  */
 9 public class Mail implements Cloneable{
10
11     private String name;
12
13     private String context;
14
15     private String title;
16
17     private String address;
18
19
20     public Mail(String name, String context, String title, String address) {
21         super();
22         this.name = name;
23         this.context = context;
24         this.title = title;
25         this.address = address;
26     }
27
28
29     /**
30      * 克隆方法
31      */
32     @Override
33     protected Mail clone() throws CloneNotSupportedException {
34         Mail mail1=null;
35         mail1=(Mail) super.clone();
36         return mail1;
37     }
38
39
40     public String getName() {
41         return name;
42     }
43
44     public void setName(String name) {
45         this.name = name;
46     }
47
48     public String getContext() {
49         return context;
50     }
51
52     public void setContext(String context) {
53         this.context = context;
54     }
55
56     public String getTitle() {
57         return title;
58     }
59
60     public void setTitle(String title) {
61         this.title = title;
62     }
63
64     public String getAddress() {
65         return address;
66     }
67
68     public void setAddress(String address) {
69         this.address = address;
70     }
71
72
73 }

View Code

【2】浅拷贝

 1 package com.yeepay.sxf.template8;
 2
 3 import java.util.ArrayList;
 4 import java.util.List;
 5
 6
 7
 8 /**
 9  * 浅拷贝
10  * @author sxf
11  * (1)JVM做了一个偷懒的拷贝动作,Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象
12 的内部元素地址,这种拷贝就叫做浅拷贝
13  *(2)非常不安全
14  *
15  */
16 public class Thing implements Cloneable {
17
18     private List<String> list=new ArrayList<String>();
19
20
21     @Override
22     protected Thing clone() throws CloneNotSupportedException {
23         Thing thing=null;
24         thing=(Thing) super.clone();
25         return thing;
26     }
27
28     public List<String> getList() {
29         return list;
30     }
31
32     public void setList(String a) {
33         this.list.add(a);
34     }
35
36
37 }

View Code

【3】深拷贝

 1 package com.yeepay.sxf.template8;
 2
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 /**
 6  * 深层次拷贝
 7  * (1)深拷贝还有一种实现方式就是通过自己写二进制流来操作对象,然后实现对象的深拷贝,这个大家有时间自己实现一下
 8  * (2)深拷贝和浅拷贝建议不要混合使用,特别是在涉及类的继承时,父类有多个引用的情况就非常复杂,建议的方案是深拷贝和浅拷贝分开实现。
 9  * @author sxf
10  *
11  */
12 public class Thing2 implements     Cloneable {
13     private ArrayList<String> list=new ArrayList<String>();
14
15
16     @Override
17     protected Thing2 clone() throws CloneNotSupportedException {
18         Thing2 thing2=null;
19         thing2=(Thing2) super.clone();
20         thing2.list=(ArrayList<String>) this.list.clone();
21         return thing2;
22     }
23
24     public List<String> getList() {
25         return list;
26     }
27
28     public void setList(String a) {
29         this.list.add(a);
30     }
31
32 }

View Code

【4】客户端测试

 1 package com.yeepay.sxf.template8;
 2
 3 import java.util.List;
 4
 5
 6 public class ClientTest {
 7
 8     public static void main(String[] args) throws CloneNotSupportedException {
 9         //test01();
10         //test02();
11         test03();
12     }
13
14     /**
15      * 原型模式:模板测试
16      * @throws CloneNotSupportedException
17      */
18     public static void test01() throws CloneNotSupportedException{
19         Mail mail=new Mail("sxf", "go smx", "emailtosxf", "sxf@163.com");//ClientTest.main()com.yeepay.sxf.template8.Mail@2a5330
20         System.out.println("ClientTest.main()"+mail.toString());
21         Mail mail2=mail.clone();
22         System.out.println("ClientTest.main()"+mail2.toString());//ClientTest.main()com.yeepay.sxf.template8.Mail@18872380
23
24     }
25     /**
26      * 原型模式:浅拷贝
27      * @throws CloneNotSupportedException
28      */
29     public static void test02() throws CloneNotSupportedException{
30         Thing thing1=new Thing();
31         thing1.setList("小李");
32         Thing thing2=thing1.clone();
33         thing1.setList("小张");
34         List<String> t=thing1.getList();
35         List<String> t2=thing2.getList();
36         for (int i = 0; i < t.size(); i++) {
37             System.out.println("ClientTest.test02(t==>)"+t.get(i));
38         }
39         for (int i = 0; i < t2.size(); i++) {
40             System.out.println("ClientTest.test02(t2==>)"+t2.get(i));
41         }
42         //ClientTest.test02(t==>)小李
43         //ClientTest.test02(t==>)小张
44         //ClientTest.test02(t2==>)小李
45         //ClientTest.test02(t2==>)小张
46     }
47
48     /**
49      * 原型模式:深拷贝
50      * @throws CloneNotSupportedException
51      */
52     public static void test03() throws CloneNotSupportedException{
53         Thing2 thing2a=new Thing2();
54         thing2a.setList("小李");
55         Thing2 thing2b=thing2a.clone();
56         thing2a.setList("小张");
57         List<String> t=thing2a.getList();
58         List<String> t2=thing2b.getList();
59         for (int i = 0; i < t.size(); i++) {
60             System.out.println("ClientTest.test02(t==>)"+t.get(i));
61         }
62         for (int i = 0; i < t2.size(); i++) {
63             System.out.println("ClientTest.test02(t2==>)"+t2.get(i));
64         }
65         //ClientTest.test02(t==>)小李
66         //ClientTest.test02(t==>)小张
67         //ClientTest.test02(t2==>)小李
68     }
69 }

View Code

 

时间: 2024-10-22 06:49:44

设计模式之禅之设计模式-原型模式的相关文章

.NET设计模式(6):原型模式(Prototype Pattern)

概述 在软件系统中,有时候面临的产品类是动态变化的,而且这个产品类具有一定的等级结构.这时如果用工厂模式,则与产品类等级结构平行的工厂方法类也要随着这种变化而变化,显然不大合适.那么如何封装这种动态的变化?从而使依赖于这些易变对象的客户程序不随着产品类变化? 意图 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 结构图 Prototype模式结构图 生活中的例子 Prototype模式使用原型实例指定创建对象的种类.新产品的原型通常是先于全部产品建立的,这样的原型是被动的,并不

设计模式之禅之设计模式-备忘录模式

<一:备忘录模式的定义        --->备忘录模式(Memento Pattern)提供了一种弥补真实世界缺陷的方法,让"后悔药"在程序的世界中真实可行        --->在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态.        --->通俗地说,备忘录模式就是一个对象的备份模式,提供了一种程序数据的备份方法 二:备忘录模式的角色 ● Originator发起人角色      

设计模式的C++实现之原型模式

解决的问题: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.这个其实和C++的拷贝构 造函数的作用是一致的,实际上就是动态抽取当前对象运行时的状态. 类图结构: 客户(Client)角色:客户类 提出创建对象的请求. 抽象原型(Prototype)角色:这是一个抽象角色,通常由一个C#接口或抽象类实现.此角色给出 所有的具体原型类所需的接口.在C#中,抽象原型角色通常实现了ICloneable接口. 具体原型(Concrete Prototype) 角色:被复制的对象.此角色需

设计模式之禅之设计模式-桥梁模式

一:桥梁模式定义        --->桥梁模式(Bridge Pattern)也叫做桥接模式,是一个比较简单的模式        --->将抽象和实现解耦,使得两者可以独立地变化. 二:桥梁模式角色 ● Abstraction--抽象化角色        它的主要职责是定义出该角色的行为,同时保存一个对实现化角色的引用,该角色一般是抽象类.● Implementor--实现化角色        它是接口或者抽象类,定义角色必需的行为和属性.● RefinedAbstraction--修正抽象

设计模式学习笔记(十一)—Prototype原型模式

Prototype模式的意图是: 通过给出一个原型对象来指明所要创建的对象类型,然后用复制这个原型对象的办法创建出更多的同类型对象. 在java的类库中已经实现了这一模式,只要你定义的类实现了Cloneable接口,用这个类所创建的对象可以做为原型对象进而克隆出更多的同类型的对象.下面举个例子,来介绍简单的介绍一下它的使用. import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java

设计模式之禅之设计模式-装饰者模式

一:装饰模式的定义        --->动态地给一个对象添加一些额外的职责.就增加功能来说,装饰模式相比生成子类更为灵活.        --->如果大家还记得代理模式,那么很容易看懂这个类图,装饰类的作用也就是一个特殊的代理类.        --->在装饰模式中,必然有一个最基本.最核心.最原始的接口或抽象类充当Component抽象构件 二:装饰模式的角色        ● Component抽象构件                Component是一个接口或者是抽象类,就是定

设计模式之禅之设计模式-策略模式

一:策略模式的定义        --->是一种比较简单的模式,也叫做政策模式        --->定义一组算法,将每个算法都封装起来,并且使它们之间可以互换 二:策略模式的三个角色 ● Context封装角色        --->它也叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略.算法的直接访问,封装可能存在的变化.● Strategy抽象策略角色        --->策略.算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性● ConcreteStr

设计模式之禅之设计模式-迭代器模式

一:迭代器模式的定义        --->迭代器模式(Iterator Pattern)目前已经是一个没落的模式,基本上没人会单独写一个迭代器,除非是产品性质的开发        --->它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节.        --->迭代器是为容器服务的,那什么是容器呢? 能容纳对象的所有类型都可以称之为容器,例如Collection集合类型.Set类型等,迭代器模式就是为解决遍历这些容器中的元素而诞生的        --->迭

设计模式之禅之设计模式-模板方法模式

一:模板方法模式的定义        --->定义一个操作中的算法的框架,而将一些步骤延迟到子类中.使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤.        --->模板方法模式确实非常简单,仅仅使用了Java的继承机制,但它是一个应用非常广泛的模式.其中,AbstractClass叫做抽象模板,它的方法分为两类:        ● 基本方法                基本方法也叫做基本操作,是由子类实现的方法,并且在模板方法被调用.        ● 模板方法