原型模式

2019-11-27  本文已影响0人  缓慢移动的蜗牛

原型模式定义

定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

通用类图如下:

原型模式通用类图、.png
public class PrototypeClass  implements Cloneable{
     //覆写父类Object方法
     @Override
     public PrototypeClass clone(){
             PrototypeClass prototypeClass = null;
             try {
                    prototypeClass = (PrototypeClass)super.clone();
             } catch (CloneNotSupportedException e) {
                    //异常处理
             }
             return prototypeClass;
     }
}

实现一个接口,然后重写clone方法,就完成了原型模式!很简单啊

示例

模拟信用卡中心批量发送广告信的场景

原型模式-发广告邮件示例.png

广告信模板

public class AdvTemplate {
     //广告信名称
     private String advSubject ="XX银行国庆信用卡抽奖活动";
     //广告信内容
     private String advContext = "国庆抽奖活动通知:只要刷卡就送你一百万!...";
     //取得广告信的名称
     public String getAdvSubject(){
             return this.advSubject;
     }
     //取得广告信的内容
     public String getAdvContext(){
             return this.advContext;
     }
}

邮件类

public class Mail implements Cloneable{
     //收件人
     private String receiver;
     //邮件名称
     private String subject;
     //称谓
     private String appellation;
     //邮件内容
     private String contxt;
     //邮件的尾部,一般都是加上"XXX版权所有"等信息
     private String tail;
     //构造函数
     public Mail(AdvTemplate advTemplate){
             this.contxt = advTemplate.getAdvContext();
             this.subject = advTemplate.getAdvSubject();
     }
     @Override
     public Mail clone(){
             Mail mail =null;
             try {
                    mail = (Mail)super.clone();
             } catch (CloneNotSupportedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
             }
             return mail;
     }
     //以下为getter/setter方法
}

场景类

public class Client {
     //发送账单的数量,这个值是从数据库中获得
     private static int MAX_COUNT = 6;
     public static void main(String[] args) {
             //模拟发送邮件
             int i=0;
             //把模板定义出来,这个是从数据中获得
             Mail mail = new Mail(new AdvTemplate());
             mail.setTail("XX银行版权所有");
             while(i<MAX_COUNT){
                    //以下是每封邮件不同的地方
                    Mail cloneMail = mail.clone();
                    cloneMail.setAppellation(getRandString(5)+" 先生(女士)");
                    cloneMail.setReceiver(getRandString(5)+"@"+getRandString(8)+".com");
                    //然后发送邮件
                    sendMail(cloneMail);
                    i++;
             }
     }
    
    //发送邮件
     public static void sendMail(Mail mail){
             System.out.println("标题:"+mail.getSubject() + "\t收件人:
                "+mail.getReceiver()+"\t...发送成功!");
     }
     //获得指定长度的随机字符串
     public static String getRandString(int maxLength){
             String source ="abcdefghijklmnopqrskuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
             StringBuffer sb = new StringBuffer();
             Random rand = new Random();
             for(int i=0;i&lt;maxLength;i++){
                     sb.append(source.charAt(rand.nextInt(source.length())));
             }
             return sb.toString();
     }
}

sendMail()即使是多线程下也是没有关系的,mail.clone()这个方法,把对象复制一份,产生一个新对象,和原有对象一样,然后再修改细节的数据。这种不通过new关键字来产生一个对象,而是通过对象复制来实现的模式就叫做原型模式。

原型模式的应用

优点

使用场景

使用注意事项

构造函数不会执行

public class Thing implements Cloneable{
    public Thing() {
        System.out.println("----构造函数----");
    }

    @Override
    protected Thing clone() {
        Thing thing = null;
        try {
            thing = (Thing) super.clone();
        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return thing;
    }

    public static void main(String[] args) {
        Thing t = new Thing();
        t.clone();
        /*
        执行结果:
        ----构造函数----
        */
    }
}

对象拷贝时构造函数确实没有被执行,这点从原理来讲也是可以讲得通的,Object类的clone方法的原理是从内存中(具体地说就是堆内存)以二进制流的方式进行拷贝,重新分配一个内存块,那构造函数没有被执行也是非常正常的了。

浅拷贝与深拷贝的问题

clone与final

对象的clone与对象内的final关键字是有冲突的

public class Thing implements Cloneable{
     //定义一个私有变量
     private ArrayList&lt;String&gt; arrayList = new ArrayList&lt;String&gt;();
     @Override
     public Thing clone(){
             Thing thing=null;
             try {
                    thing = (Thing)super.clone();
                    //编译器报错
                    thing.arrayList = (ArrayList&lt;String&gt;)this.arrayList.clone();
             } catch (CloneNotSupportedException e) {
                    e.printStackTrace();
             }
             return thing;
     }
}

浅拷贝

浅拷贝介绍

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。

浅拷贝的特点
  1. 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个 .
  2. 对于引用类型,比如数组或者类对象,因为引用类型是引用传递,所以浅拷贝只是把内存地址赋值给了成员变量,它们指向了同一内存空间。改变其中一个,会对另外一个也产生影响。

图示:

浅拷贝图示.png
浅拷贝的实现

实现对象拷贝的类,需要实现 Cloneable 接口,并覆写 clone() 方法。

public class Subject {
    private String name;
    //getter setter方法
}

public class Student implements Cloneable {
    //引用类型
    private Subject subject;
    //基础数据类型
    private String name;
    private int age;
    //getter setter方法

    /**
     *  重写clone()方法
     * @return
     */
    @Override
    public Object clone() {
        //浅拷贝
        try {
            // 直接调用父类的clone()方法
            return super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }

    @Override
    public String toString() {
        return "[Student: " + this.hashCode() + ",subject:" + subject + ",name:" + name + ",age:" + age + "]";
    }
}

测试类

public class Client{
    public static void main(String[] args) {
        Subject subject = new Subject();
        subject.setName("xxxxxx");

        Student s = new Student();
        s.setSubject(subject);
        s.setAge(20);
        s.setName("sssss");

        Object clone = s.clone();

        System.out.println(s);
        System.out.println(clone);
    }
}

结果:

subject的内部地址一样,所以clone后的对象和原来对象共享subject对象

Student{subject=Subject@7506e922, name='sssss', age=20}
Student{subject=Subject@7506e922, name='sssss', age=20}

深拷贝

深拷贝介绍

深拷贝,在拷贝引用类型成员变量时,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。

深拷贝特点
  1. 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。
  2. 对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。
  3. 对于有多层对象的,每个对象都需要实现 Cloneable 并重写 clone() 方法,进而实现了对象的串行层层拷贝。
  4. 深拷贝相比于浅拷贝速度较慢并且花销较大。

图示:

深拷贝图示.png
深拷贝的实现

示例1

public class Subject implements Cloneable {

    private String name;
    //getter setter

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //Subject 如果也有引用类型的成员属性,也应该和 Student 类一样实现
        return super.clone();
    }
}

public class Student implements Cloneable {

    //引用类型
    private Subject subject;
    //基础数据类型
    private String name;
    private int age;

    //getter setter

    /**
     *  重写clone()方法
     * @return
     */
    @Override
    public Object clone() {
        //深拷贝
        try {
            // 直接调用父类的clone()方法
            Student student = (Student) super.clone();
            student.subject = (Subject) subject.clone();
            return student;
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }

    @Override
    public String toString() {
        return "[Student: " + this.hashCode() + ",subject:" + subject + ",name:" + name + ",age:" + age + "]";
    }
}

示例2

public class Thing implements Cloneable{
     //定义一个私有变量
     private ArrayList<String> arrayList = new ArrayList<String>();
     @Override
     public Thing clone(){
             Thing thing=null;
             try {
                    thing = (Thing)super.clone();
                    thing.arrayList = (ArrayList&lt;String&gt;)this.arrayList.clone();
             } catch (CloneNotSupportedException e) {
                    e.printStackTrace();
             }
             return thing;
     }
}
上一篇下一篇

猜你喜欢

热点阅读