精华内容
下载资源
问答
  • 原型模式多用于创建复杂或者构造耗时实例,因为这种情况下,复制一个已经存在实例可使程序运行更高效。原型模式是用于创建重复对象,同时又能保证性能。,原型模式提供了一种创建对象最佳方式。这种模式是...

    概念

    原型模式是一个创建型的模式。用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。原型模式多用于创建复杂的或者构造耗时的实例,因为这种情况下,复制一个已经存在的实例可使程序运行更高效。原型模式是用于创建重复的对象,同时又能保证性能。,原型模式提供了一种创建对象的最佳方式。这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。

    工作原理

    通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实现创建,即 对象.clone()

    传统方式克隆和原型模式比较

    传统方式:

    public class Person {
        //名字
        private String name;
        //年龄
        private int age;
        //肤色
        private String color;
        //性别
        private String sex;
    
        public Person(String name, int age, String color, String sex) {
            super();
            this.name = name;
            this.age = age;
            this.color = color;
            this.sex = sex;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getColor() {
            return color;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", color='" + color + '\'' +
                    ", sex=" + sex +
                    '}';
        }
    }
    
    public class PrototypeDemo {
        public static void main(String[] args) {
            Person person = new Person("张三", 20, "黄色","男");
            Person person2 = new Person(person.getName(), person.getAge(), person.getColor(), person.getSex());
            Person person3 = new Person(person.getName(), person.getAge(), person.getColor(), person.getSex());
            System.out.println(person.toString());
            System.out.println(person2.toString());
            System.out.println(person3.toString());
        }
    }
    

    输出

    Person{name='张三', age=12, color='黄色', sex=男}
    Person{name='张三', age=12, color='黄色', sex=男}
    Person{name='张三', age=12, color='黄色', sex=男}
    

    优点:

    好理解,简单易操作

    缺点:

    1.在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低。

    2.总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活。

    简单原型模式方式:

    public class Person2  implements Cloneable {
        //名字
        private String name;
        //年龄
        private int age;
        //肤色
        private String color;
        //性别
        private String sex;
    
        public Person2(String name, int age, String color, String sex) {
            super();
            this.name = name;
            this.age = age;
            this.color = color;
            this.sex = sex;
        }
    
        /**
         * 克隆该实例,使用默认的clone方法来完成
         */
        @Override
        protected Person2 clone() {
            Person2 person2 = null;
            try {
                person2 = (Person2)super.clone();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return person2;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getColor() {
            return color;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
        @Override
        public String toString() {
            return "Person2{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", color='" + color + '\'' +
                    ", sex=" + sex +
                    '}';
        }
    }
    
    public class PrototypeDemo2 {
        public static void main(String[] args) {
            Person2 person = new Person2("张三", 20, "黄色","男");
            Person2 person2 = person.clone();
            Person2 person3 = person.clone();
            System.out.println(person.toString());
            System.out.println(person2.toString());
            System.out.println(person3.toString());
        }
    }
    

    输出:

    Person2{name='张三', age=20, color='黄色', sex=男}
    Person2{name='张三', age=20, color='黄色', sex=男}
    Person2{name='张三', age=20, color='黄色', sex=男}
    

    除了简单原型模式,对象的拷贝还有深拷贝,浅拷贝:具体看文章:

    深拷贝和浅拷贝详解以及实例

    优点:
    1.当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。
    2.扩展性较好,由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少产品类对原有系统都没有任何影响。
    3.原型模式提供了简化的创建结构,工厂方法模式常常需要有一个与产品类等级结构相同的工厂等级结构,而原型模式就不需要这样,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品。
    4.可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(如恢复到某一历史状态),可辅助实现撤销操作。

    5.性能提高。

    6.逃避构造函数的约束。

    缺点:
    1.配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
    2.必须实现 Cloneable 接口。

    如果大家对java架构相关感兴趣,可以关注下面公众号,会持续更新java基础面试题, netty, spring boot,spring cloud等系列文章,一系列干货随时送达, 超神之路从此展开, BTAJ不再是梦想!

    架构殿堂

    展开全文
  • 设计模式-原型模式

    2020-03-03 22:08:12
    设计模式-原型模式 原型模式分为:不通过构造函数的方式创建对象,也就是copy对象。 浅克隆和深克隆两种 原型模式的有点: ...原型模式的缺点: 必须配备克隆方法; 当对已有类进行改造的时候,需要修改代码,...

    设计模式-原型模式
    原型模式分为:不通过构造函数的方式创建对象,也就是copy对象。
    浅克隆和深克隆两种
    原型模式的有点:
    性能优良,Java自带的原型模式,是基于内存二进制流的拷贝,比直接new一个对象性能上提升了许多;
    可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,简化了创建过程。
    原型模式的缺点:
    必须配备克隆方法;
    当对已有类进行改造的时候,需要修改代码,违反了开闭原则。
    一、通常写法:
    克隆的接口

    public interface IPrototype<T> {
        T clone();
    }
    

    实现克隆的接口,然后硬编码,重写clone方法

    public class ConcretePrototype implements IPrototype {
        private String name;
        private int age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "ConcretePrototype{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        @Override
        public ConcretePrototype clone() {
            ConcretePrototype prototype  =  new  ConcretePrototype();
            prototype.setAge(this.getAge());
            prototype.setName(this.getName());
            return prototype;
        }
    }
    

    测试类

    public class Client {
        public static void main(String[] args) {
    
            ConcretePrototype prototype = new ConcretePrototype();
            prototype.setName("张三");
            prototype.setAge(18);
    
            System.out.println("原型对象: " + prototype);
    
            ConcretePrototype clone = prototype.clone();
            System.out.println("克隆对象: " + clone);
        }
    }
    

    运行结果

    原型对象: ConcretePrototype{name='张三', age=18}
    克隆对象: ConcretePrototype{name='张三', age=18}
    

    二、实现Cloneable接口的浅克隆
    只要实现Cloneable接口的都是浅克隆

    public class ConcretePrototype implements Cloneable {
        private String name;
        private int age;
        private List<String> hobbs;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public List<String> getHobbs() {
            return hobbs;
        }
    
        public void setHobbs(List<String> hobbs) {
            this.hobbs = hobbs;
        }
    
        @Override
        public String toString() {
            return "ConcretePrototype{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", hobbs=" + hobbs +
                    '}';
        }
    
        @Override
        public ConcretePrototype clone() {
            try {
                return (ConcretePrototype) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    

    三、深克隆

    public class ConcretePrototype implements Cloneable,Serializable{
        private String name;
        private int age;
        private List<String> hobbs;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public List<String> getHobbs() {
            return hobbs;
        }
    
        public void setHobbs(List<String> hobbs) {
            this.hobbs = hobbs;
        }
    
        @Override
        public String toString() {
            return "ConcretePrototype{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", hobbs=" + hobbs +
                    '}';
        }
        // 浅克隆
        @Override
        public ConcretePrototype clone() {
            try {
                return (ConcretePrototype) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return null;
        }
    	// 深克隆
        public ConcretePrototype deepClone() {
            try {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bos);
                oos.writeObject(this);
    
                ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(bis);
                return (ConcretePrototype) ois.readObject();
            } catch (Exception e) {
                e.getMessage();
            }
            return null;
        }
        // 深克隆2
        public ConcretePrototype deepJsonClone() {
            JSONObject obj = (JSONObject) JSONObject.toJSON(this);
            ConcretePrototype prototype = JSONObject.toJavaObject(obj, ConcretePrototype.class);
            return prototype;
        }
    }
    
    展开全文
  • 个性化电子账单使用原型模式前使用原型模式后原型模式的优缺点原型模式的优点原型模式的缺点深克隆与浅克隆浅克隆深克隆原型模式的应用场景 什么是原型模式? 原型模式是一个比较简单,但应用频率比较高的设计模式...


    什么是原型模式?

    原型模式是一个比较简单,但应用频率比较高的设计模式。

    Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.(用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对 象。)

    原型模式的通用类图如下:

    图13-1:原型模式通用类图

    在这里插入图片描述

    原型模式的核心是一个clone方法,通过该方法进行对象的拷贝,Java 提供了一个Cloneable接口来标示这个对象是可拷贝的,Cloneable接口的作用是标记,在JVM中具有这个标记的对象才有可能被拷贝。那怎么才能从“有可能被拷贝”转换为“可以被拷贝”呢?方法是覆盖 clone()方法:

    @Override public Mail clone(){}
    

    在clone()方法上增加了一个注解@Override——因为覆写了Object类的clone方法。

    在Java中原型模式非常简单,通用源码如下:

    public class PrototypeClass implements Cloneable{ 
       //覆写父类Object方法 
       @Override public PrototypeClass clone(){
          PrototypeClass prototypeClass = null; 
          try { 
            prototypeClass = (PrototypeClass)super.clone(); 
           } catch (CloneNotSupportedException e) { 
             //异常处理 
           }
           return prototypeClass; 
       } 
    }
    

    个性化电子账单

    现在有这样的业务场景:

    每到月初的时候银行会给信用卡用户以邮件的方式发送一份电子账单,包含用户的消费情况、积分等,当然了,还有比较讨厌的广告信。出于个性化服务和投递成功率的考虑,广告信也作为电子账单系统的一个子功能。

    这个功能大概这么实现:指定一个模板,从数据库中把客户的信息一个一个地取出,放到模板中生成一份完整的邮件, 然后由发送机进行发送处理。


    使用原型模式前

    结合上面的实现思路,相应的类图如下:

    图13-2:发送电子账单类图

    在这里插入图片描述

    AdvTemplate是广告信的模板,一般都是从数据库取出,生成一个BO或者是 DTO,这里使用一个静态的值来作代表;Mail是邮件类,发送机发送的就是这个类。

    • AdvTemplate类:
    /**
     * @author 三分恶
     * @date 2020年5月14日 
     * @description 广告模板类
     */
    public class AdvTemplate {
    	//广告信名称
    	private String advSubject ="XX银行国庆信用卡抽奖活动";
    	//广告信内容
    	private String advContext = "国庆抽奖活动通知:只要刷卡就送你一百万!...";
    	
    	public String getAdvSubject() {
    		return advSubject;
    	}
    	public String getAdvContext() {
    		return advContext;
    	}
    	
    }
    
    • Mail类:
    /**
     * @author 三分恶
     * @date 2020年5月14日 
     * @description 邮件类 
     */
    public class Mail {
    	//收件人
    	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();
    	}
    	//省略getter、setter方法
    }
    
    
    • Client场景类:
    /**
     * @author 三分恶
     * @date 2020年5月14日
     * @description 场景类
     */
    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.setAppellation(getRandString(5) + " 先生(女士)");
    			mail.setReceiver(getRandString(5) + "@" + getRandString(8) + ".com");
    			// 然后发送邮件
    			sendMail(mail);
    			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 < maxLength; i++) {
    			sb.append(source.charAt(rand.nextInt(source.length())));
    		}
    		return sb.toString();
    	}
    
    }
    

    运行结果:

    在这里插入图片描述

    OK,发送广告信的功能到此实现了。

    但是存在一个问题,这个程序时单线程的,按照一封邮件发出去需要0.02秒,600万封邮件需要33个小时,也就是一个整天都发送不完,今天的没发送完,明天的账单又产生了。

    如果用多线程的方式呢?那么线程安全的问题又来了。产生第一封邮件对象,放到线程1中运行,还没有发送出去;线程2也启动了,直接就把邮件对 象mail的收件人地址和称谓修改了。

    解决的办法有很多,其中一种就是通过原型模式。


    使用原型模式后

    类图稍作修改:

    图13-3:发送电子账单类图

    在这里插入图片描述

    • Mail类实现Cloneable接口,覆写clone()方法:
    /**
     * @author 三分恶
     * @date 2020年5月14日 
     * @description 邮件类 
     */
    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();
    	}
    	
    	/**
    	 * 覆写clone方法
    	 */
    	@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方法
    }
    
    • Client场景类的修改:
    public static void main(String[] args) {
    		// 模拟发送邮件
    		int i = 0;
    		// 把模板定义出来
    		Mail mail = new Mail(new AdvTemplate());
    		mail.setTail("XX银行版权所有");
    		while (i < MAX_COUNT) {
    			// 以下是每封邮件不同的地方
    			//这里使用clone方法clone对象
    			Mail cloneMail = mail.clone();
    			//使用clone的对象
    			cloneMail.setAppellation(getRandString(5) + " 先生(女士)");
    			cloneMail.setReceiver(getRandString(5) + "@" + getRandString(8) + ".com");
    			// 然后发送邮件
    			sendMail(mail);
    			i++;
    		}
    	}
    

    在设置邮件不同属性的地方通过clone的方式产生一个新的对象,然后再修改细节的数据,如设置称谓、设置收件人地址,这样即使是多线程也不受影响。


    原型模式的优缺点

    原型模式的优点

    原型模式的主要优点如下:

    • 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。
    • 原型模式提供了简化的创建结构,工厂方法模式常常需要有一个与产品类等级结构相同的 工厂等级结构,而原型模式就不需要这样,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品。
    • 可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起 来,以便在需要的时候使用(如恢复到某一历史状态),可辅助实现撤销操作。

    原型模式的缺点

    原型模式的主要缺点如下:

    • 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进 行改造时,需要修改源代码,违背了“开闭原则”。
    • 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了 实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦。

    深克隆与浅克隆

    在Java语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括 int、double、byte、boolean、char等简单数据类型,引用类型包括类、接口、数组等复杂类 型。浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制。


    浅克隆

    在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的 成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆 对象的成员变量指向相同的内存地址。简单来说,在浅克隆中,当对象被复制时只复制它本 身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。

    在这里插入图片描述

    来看一个实例:

    • 在Thing类中定义一个私有变量arrayLis,类型为ArrayList,然后通过setValue和getValue 分别进行设置和取值
    /**
     * @author 三分恶
     * @date 2020年5月14日 
     * @description  
     */
    public class Thing implements Cloneable{
    	//定义一个私有变量 
    	private ArrayList<String> arrayList = new ArrayList<String>();
    
    	@Override
    	public Thing clone() {
    		Thing thing = null;
    		try {
    			thing = (Thing) super.clone();
    		} catch (CloneNotSupportedException e) {
    			e.printStackTrace();
    		}
    		return thing;
    	}
    
    	//设置HashMap的值 
    	public void setValue(String value){ 
    		this.arrayList.add(value); }
    
    	// 取得arrayList的值
    	public ArrayList<String> getValue() {
    		return this.arrayList;
    	}	
    	
    }
    
    • 在场景类中克隆
    /**
     * @author 三分恶
     * @date 2020年5月14日 
     * @description  浅克隆测试
     */
    public class ShallowCloneClient {
    	public static void main(String[] args) {
    		// 产生一个对象
    		Thing thing = new Thing();
    		// 设置一个值
    		thing.setValue("二锤子");
    		// 拷贝一个对象
    		Thing cloneThing = thing.clone();
    		cloneThing.setValue("三棒子");
    		System.out.println(thing.getValue());
    	} 
    }
    

    在这个例子中,对象thing和cloneThing的arrayList都指向了同一个地址,所以运行结果:

    在这里插入图片描述

    这里就存在风险,两个对象共享了一个私有变量,都能对这个变量进行修改。

    使用原型模式时,引用的成员变量必须满足两个条件才不会被克隆:一是类的成 员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原始类型或不可 变对象。


    深克隆

    在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象, 深克隆将原型对象的所有引用对象也复制一份给克隆对象。简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。

    在这里插入图片描述

    将Thing类的clone方法进行修改:

    @Override
    	public Thing clone() {
    		Thing thing = null;
    		try {
    			thing = (Thing) super.clone();
    			//对私有的类变量进行独立的拷贝
    			thing.arrayList = (ArrayList<String>)this.arrayList.clone();
    		} catch (CloneNotSupportedException e) {
    			e.printStackTrace();
    		}
    		return thing;
    	}
    
    

    改动很少,对私有的类变量进行独立的拷贝,这样一来,两个对象的arrayList的指向地址就不一样了。
    在这里插入图片描述

    还可以通过序列化的方式实现,序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存 中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。


    原型模式的应用场景

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

    • 性能和安全要求的场景
      通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。

    • 一个对象多个修改者的场景 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑 使用原型模式拷贝多个对象供调用者使用。

    在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的 方法创建一个对象,然后由工厂方法提供给调用者。




    ⇐⇐ 设计模式—— 十二 :代理模式

    ⇒⇒ 设计模式—— 十四 :中介者模式

    参考:
    【1】:《设计模式之禅》
    【2】:《design-pattern-java》
    【3】:《大话设计模式》
    【4】:《设计模式之禅》

    展开全文
  • 一.定义:用一个已经创建的...2.原型模式的核心思想是,通过拷贝指定的“原型实例(被克隆的对象)”,创建跟该对象一样的新对象。简单理解就是“克隆指定对象”。三.优缺点1.优点:(1)java自带的原型模式基于内...

    dbd9095006b391069f7462706b0d3e17.png

    一.定义:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。

    二.概述

    1.当系统中需要大量创建相同或者相似的对象时,就可以通过“原型设计模式”来实现。原型模式是“创建型设计模式”中的一种。

    2.原型模式的核心思想是,通过拷贝指定的“原型实例(被克隆的对象)”,创建跟该对象一样的新对象。简单理解就是“克隆指定对象”。

    三.优缺点

    1.优点:

    (1)java自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。

    (2)可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。

    2.缺点:

    (1)需要为每一个类都配置一个 clone 方法

    (2)clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。

    (3)当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。

    四.结构

    1.抽象原型类:规定了具体原型对象必须实现的接口。

    2.具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。

    3.访问类:使用具体原型类中的 clone() 方法来复制新的对象。

    4.结构图

    e35df54a8ca0ff4c5187b7605338dd5d.png

    五.深拷贝和浅拷贝

    1.浅拷贝:

    (1)当类的成员变量是基本数据类型时,浅拷贝会复制该属性的值赋值给新对象。

    (2)当成员变量是引用数据类型时,浅拷贝复制的是引用数据类型的地址值。它跟原对象指向的是一个内存地址,当变量修改,就相当于所有的都改了

    2.深拷贝:

    (1)当类的成员变量是基本数据类型时,浅拷贝会复制该属性的值赋值给新对象。

    (2)当成员变量是引用数据类型时,深拷贝会给是引用数据类型的成员变量申请储存空间,并复制引用数据类型成员变量的对象。所以就不会对拷贝出的对象造成影响。

    六:具体实现

    1.需要拷贝的原型类必须实现"java.lang.Cloneable"接口,然后重写Object类中的clone方法,从而才可以实现类的拷贝。

    2.Cloneable是一个“标记接口”,所谓的标记接口就是该接口中没有任何内容。标记接口的作用就是为了给所有实现了该接口的类赋予一种特殊的标志。

    3.只有当一个类实现了Cloneable接口后,该类才会被赋予调用重写自Object类的clone方法得权利。否则会抛出“CloneNotSupportedException”异常。

    4.代码

    //具体原型类
    class Realizetype implements Cloneable{
       Realizetype(){
          System.out.println("具体原型创建成功!");
       }
       //重写Object类的clone方法
       public Object clone() throws CloneNotSupportedException{
          System.out.println("具体原型复制成功!");
          return (Realizetype)super.clone();
       }
    }
    
    //原型模式的测试类
    public class PrototypeTest{
       public static void main(String[] args)throws CloneNotSupportedException{
          Realizetype obj1=new Realizetype();//原型创建
          Realizetype obj2=(Realizetype)obj1.clone();//原型复制
          System.out.println("obj1==obj2?"+(obj1==obj2));//结果为false
       }
    }

    七.原型模式的应用场景

    1.对象之间相同或相似,即只是个别的几个属性不同的时候。

    2.创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。

    3.创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。

    4.系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。

    展开全文
  • 1 基础知识 1.1 标准定义 原型模式标准定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 1.2 分析和说明 原型模式是指用原型实例...原型模式的缺点是每一个类都必须配备一个克隆方法。 Pr
  • 5、了解原型模式的优、缺点6、掌握建造者模式和工厂模式的区别。5.2.内容定位1、已了解并掌握工厂模式的人群。2、已了解并掌握单例模式。3、听说过原型模式,但不知道应用场景的人群。5.3.定义原型模式(Prototype...
  • 原型模式的缺点: * 虽然省略了为构造函数传递初始化参数这一环节,结果所有的实例都有了默认的相同的属性值。会带来一些不方便,但主要问题还是由其共享的本质所导致的。 * 原型中所有的属性是被很多实例共享的,这...
  • 原型模式总结-java版

    2019-02-08 16:36:58
    原型模式的缺点 原型模式的适用场景 原型模式的简介 在使用原型模式时,我们需要首先创建一个原型对象,再通过复制这个原型对象来创建更多同类型的对象。试想,如果连孙悟空的模样都不知道,怎么拔毛变小猴子呢?...
  • ECMA-262 把对象定义为:“无序属性集合,其属性可以包含基本值、对象或者函数。”严格来讲,这就相当于说对象是一组没有特定顺序值。 对象每个属性或方法都有一个名字,而每个名字都映射到一个值。正因为...
  • 原型模式

    2020-07-01 20:35:39
    示例程序3.1 Product接口 (Prototype)3.2 Manager类(Client)3.3 MessageBox类(ConcreteProtorype)3.4 UnderlinePen类(ConcreteProtorype)3.5 Main类4 原型模式的角色分析4.1 Prototype(抽象原型类)4.2 ...
  • 点击上方“java大数据修炼之道”,选择“设为星标”优质文章, 第一时间送达目录什么是原型模式?个性化电子账单使用原型模式前使用原型模式后原型模式的优缺点原型模式的优点原型模式的缺点深克...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 870
精华内容 348
关键字:

原型模式的缺点