美文网首页
原型模式

原型模式

作者: 缓慢移动的蜗牛 | 来源:发表于2019-11-27 17:39 被阅读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关键字来产生一个对象,而是通过对象复制来实现的模式就叫做原型模式。

    原型模式的应用

    优点

    • 性能优良

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

    • 逃避构造函数的约束

      这既是它的优点也是缺点,直接在内存中拷贝,构造函数不会执行的,优点就是减少了约束,缺点也是减少了约束。

    使用场景

    • 资源优化场景

      类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。

    • 性能和安全要求的场景

      通过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;
         }
    }
    

    相关文章

      网友评论

          本文标题:原型模式

          本文链接:https://www.haomeiwen.com/subject/mkqmwctx.html