精华内容
下载资源
问答
  • 原型模式

    2020-08-28 19:27:56
    原型模式 内容参考w3cSchool 目录 原型模式 UML 类图 创建 AbstractShape 类 实体类 Circle Rectangle Square 创建缓存类 ShapeCache UML 类图 抽象类AbstractShape中有两个属性:id 、 类型,一个绘制...

    原型模式

    内容参考 w3cSchool


    目录

    原型模式

    UML 类图

    创建 AbstractShape 类

    实体类 Circle Rectangle Square

    创建缓存类 ShapeCache


    UML 类图

    抽象类AbstractShape中有两个属性:id 、 类型,一个绘制图形的方法,并且继承了 Clone接口

    三个实现类:Circle  、 Square  、Rectangle

    在ShapeCache中进行对数据的缓存以及读取。ShapMap中缓存在数据库中查询到的数据【loadCache()】,getShape中深复制返回对应ID的Shape对象。即一次缓存后,在应用中多次使用不必每次查询数据库,直接在缓存中查询相关数据,如果没有数据再适时进行数据库查询。

     

    下面根据教程里的自己实现一遍

    创建 AbstractShape 类

    public abstract class Shape implements Cloneable{
    
        private String id;
        protected String type = "Unknown";
    
        /**
         * 绘制图形
         */
        abstract void draw();
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getType() {
            return type;
        }
    
        /*
         * 实现拷贝
         */
        @Override
        protected Object clone(){
            Object clone = null;
            try {
                clone = super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return clone;
        }
    }

    实体类 Circle Rectangle Square

    Circle

    public class Circle extends Shape{
    
        public Circle() {
            type = "Circle";
        }
    
        @Override
        void draw() {
            System.out.println("[Circle] Draw");
        }
    }

    Rectangle

    public class Rectangle extends Shape{
    
        public Rectangle() {
            type = "Rectangle";
        }
    
        @Override
        void draw() {
            System.out.println("[Rectangle] draw");
        }
    }

    Square

    public class Square extends Shape{
    
        public Square() {
            type = "Square";
        }
    
        @Override
        void draw() {
            System.out.println("[Square] Draw");
        }
    }

     

    创建缓存类 ShapeCache

    public class ShapeCache {
        private static Hashtable<String,Shape> shapeMap = new Hashtable<>();
    
        public static Shape getShape(String shapeId){
            Shape shapeCache = shapeMap.get(shapeId);
            return (Shape) shapeCache.clone();    //深拷贝
        }
    
        /*
         * 这里是模拟的数据,正常情况是使用数据库查询结果的缓存
         */
        public static void loadCache(){
            Circle circle = new Circle();
            Square square = new Square();
            Rectangle rectangle = new Rectangle();
            circle.setId("1");
            square.setId("2");
            rectangle.setId("3");
            shapeMap.put(circle.getId(),circle);
            shapeMap.put(square.getId(),square);
            shapeMap.put(rectangle.getId(),rectangle);
        }
    
    }

    测试运行

    public class ExecuteMain {
        public static void main(String[] args) {
            ShapeCache.loadCache();
    
            Shape shapeClone1 = ShapeCache.getShape("1");
            Shape shapeClone2 = ShapeCache.getShape("2");
            Shape shapeClone3 = ShapeCache.getShape("3");
    
            System.out.println("[Shape] "+shapeClone1.getType());
            System.out.println("[Shape] "+shapeClone2.getType());
            System.out.println("[Shape] "+shapeClone3.getType());
        }
    }
    [Shape] Circle
    [Shape] Square
    [Shape] Rectangle
    
    Process finished with exit code 0
    

     

     

    展开全文
  • 最近学习到设计模式,现总结个人学习原型模式内容。 上一篇:Java设计模式-工厂模式 文章目录定义优缺点优点缺点实现浅拷贝代码实现测试深拷贝代码实现测试字节码拷贝代码实现测试 定义 用原型实例指定创建对象的...

    最近学习到设计模式,现总结个人学习原型模式内容。
    上一篇:Java设计模式-工厂模式

    定义

    用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。理解:通过复制的方式创建的对象。原型模式明显更关心的是结果的一致性。

    优缺点

    优点

    • 向客户隐藏制造新实例的复杂性【用户对对象的创建过程不可见】
    • 在某些环境下,复制对象比创建新对象更有效【举例流程表单的流程对象】

    例如这个实体类,就成员都将近150个,如果全都通过创建对象的方式,其工作量是不可估量的。
    在这里插入图片描述

    缺点

    • 需要为被克隆的目标类添加克隆方法,当对已有类进行改造的时候,需要修改代码,违反了开闭原则。
    • 在实现深克隆【深拷贝】时需要编写较为复杂的代码,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。

    实现

    先给定两个实体类

    class Money implements Serializable,Cloneable{ 
    	private static final long serialVersionUID = -4350061171177220007L;
    	public String moneytype;
    	public Double moneynum;
    	Money(String moneytype,Double moneynum){
    		this.moneytype=moneytype;
    		this.moneynum=moneynum;
    		}
    	
    	@Override
    	protected Object clone() throws CloneNotSupportedException {
    		return super.clone();
    	}
    	
    }
    public class UserEntity implements Serializable,Cloneable{
    	private static final long serialVersionUID = 1L;
    	private String name;
    	private int age;
    	private ArrayList<String> phonenumbers ;
    	private ArrayList<Money> moneys ;
    	private Money moeny;
    }
    

    浅拷贝

    利用Object的clon方法

    代码实现

    /**
    	 * 浅拷贝仅能拷贝八大基本数据类型+String
    	 * 数据类型,其他数据类型直接引用地址
    	 * 例如这里拷贝不了用户的电话号码集合
    	 * @return
    	 */
    	public UserEntity getShallowCopy(){
    		UserEntity copyentity = null;
    		try {
    			copyentity= (UserEntity) this.clone();
    		} catch (CloneNotSupportedException e) {
    			e.printStackTrace();
    		}
    		return copyentity;
    	}
    

    测试

    可以看到对拷贝后的数据进行修改,其中修改基本类型+String类型并没有影响到原始数据,但是对对象修改明显影响到 原始数据。这个就需要说到深拷贝的实现了,需要自己对对象的所有成员,进行递归式的浅拷贝
    在这里插入图片描述

    深拷贝

    代码实现

    /**
    	 * 深拷贝,在浅拷贝了基本类型+String数据后,
    	 * 需要对引用类型单独进行拷贝。但是如果引用类型中还存在引用依然非完全深拷贝
    	 * @return
    	 */
    	public UserEntity getDeepCopy(){
    		UserEntity copyentity = null;
    		try {
    			copyentity= (UserEntity) this.clone();
    			copyentity.phonenumbers = (ArrayList<String>) this.phonenumbers.clone();
    			copyentity.moneys = (ArrayList<Money>) this.moneys.clone();
    			copyentity.moeny = (Money) this.moeny.clone();
    		} catch (CloneNotSupportedException e) {
    			e.printStackTrace();
    		}
    		return copyentity;
    	}
    

    测试

    从测试结果中,我们以及看到深拷贝需要对每一个成员中的对象进行克隆,同时自定义对象也需要实现clon方法,但是为什么对
    在这里插入图片描述

    字节码拷贝

    代码实现

     * 利用序列化与反序列化实现
    	 * @return
    	 */
    	public UserEntity getByteCopy(){
    		ByteArrayOutputStream baos = null;
    		ObjectOutputStream oos = null;
    		
    		ByteArrayInputStream bais = null;
    		ObjectInputStream   ois = null;
    		UserEntity copyentity = null;
    		try {
    			//序列化
    			baos = new ByteArrayOutputStream();
    			oos = new ObjectOutputStream(baos);
    			oos.writeObject(this);
    			
    			//反序列化
    			bais = new ByteArrayInputStream(baos.toByteArray());
    			ois = new ObjectInputStream(bais);
    			copyentity = (UserEntity)ois.readObject();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			try {
    				oos.close();
    				baos.close();
    				bais.close();
    				ois.close();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    		return copyentity;
    	}
    

    测试

    在这里插入图片描述


    日常对较大对象的拷贝尽量使用原型模式的方式,降低创建对象成本,较大对象的创建需要耗费cpu网络等,较大引起gc回收。

    以上仅为个人学习,如果错误望指出,谢谢。

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

    2014-01-11 21:09:36
    原型模式 内容:用原型的实例指定创建对象的种类,并且通过复制这些对象原型来创建新的对象。 结构: 抽象原型:其是一个接口,负责定义对象复制自身的方法。 具体原型:实现了抽象原型接口的子类,并提供了具体复制...

    原型模式

    内容:用原型的实例指定创建对象的种类,并且通过复制这些对象原型来创建新的对象。

    结构:

    抽象原型:其是一个接口,负责定义对象复制自身的方法。

    具体原型:实现了抽象原型接口的子类,并提供了具体复制自身的方法。

    UML类图:


    优点:

    原型模式对于创建新的对象过于复杂时可以提高创建新对象的效率,可以在运行时动态的保存当前的对象,也可以动态的增加和删除原型的复制品。

    适用情境:

    当对象的创建过程需要独立于其构造过程时,程序需要从现有的对象出发,得到若干状态相同但可以独立变化的新对象时。

    在Java中系统提供了clone()方法,它是Object类中的protected权限的方法,该方法可用于复制当前对象,而用户自定义的类不会与Object类在同一个包(java.lang)中,虽然所有的类都是Object的子类,但也不能直接使用其clone()方法。因此要想使用clone()方法,用户自定义的类可以重写Object类的clone()方法,然后在方法体中通过super关键字来调用Object类的clone()方法,当对象调用Object类中的方法时,JVM会逐个复制该对象的成员变量,然后创建一个新的对象返回,JVM要去调用clone()方法的对象必须实现Cloneable接口,该接口只是起标识作用,接口中没有任何方法。另外,需要注意的是,若当前对象中的成员变量也是一个对象,调用clone()方法只会复制该对象的引用,并没有复制该对象所拥有的变量,也就是未能实现完全意义上的复制。因此,若需要完全复制,则需要对当前对象中的成员变量对象也进行复制,这样才能实现完全复制(深度复制)。

    使用实例:

    //原型
    public interface Prototype {
    	public Object cloneSelf() throws CloneNotSupportedException;
    
    }
    public class Rectangle implements Cloneable {
    	private double width;
    	private double longth;
    	public Rectangle(double width, double longth){
    		this.longth = longth;
    		this.width  = width;
    	}
    	public double getAera(){
    		return this.longth * this.width;
    	}
    	public Object clone() throws CloneNotSupportedException{
    		Object object = super.clone();
    		return object;
    	}
    	public void setWidth(double width) {
    		this.width = width;
    	}
    	public void setLongth(double longth) {
    		this.longth = longth;
    	}
    	
    }
    //包含了引用型成员变量的具体原型
    public class Cube implements Cloneable, Prototype {
    	private Rectangle rectangle;
    	private double height;
    	
    	public Cube(Rectangle rectangle, double height) {
    		this.rectangle = rectangle;
    		this.height = height;
    	}
    	
    	public double getVolum(){
    		return this.rectangle.getAera() * this.height;
    	}
    
    	@Override
    	public Object cloneSelf() throws CloneNotSupportedException {
    		// TODO Auto-generated method stub
    		Cube object = (Cube)super.clone();
    		object.rectangle = (Rectangle)rectangle.clone();
    		return object;
    	}
    
    }
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		Rectangle rectangle = new Rectangle(1.0, 2.0);
    		
    		Cube cube = new Cube(rectangle, 3.0);
    		System.out.println("原型:"+cube.getVolum());
    	
    		try {
    			Cube cubecopy1 = (Cube)cube.cloneSelf();
    			System.out.println("复制份1:"+cubecopy1.getVolum());
    			rectangle.setLongth(10.0);//改动成员变量的属性值
    			Cube cubecopy2 = (Cube)cube.cloneSelf();
    			System.out.println("复制份2:"+cubecopy2.getVolum());
    			System.out.println("复制份1:"+cubecopy1.getVolum());//复制份1没有受到影响
    		} catch (CloneNotSupportedException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    
    }
    原型模式的核心是通过当前对象以复制的方式产生新的对象。

    展开全文
  • 点击上方Java进阶之道,选择设为星标优质文章,及时送达前言本文的主要内容如下:介绍原型模式示例Java语言的clone浅克隆与深克隆实现深克隆原型模式的典型应用原型模式原型模式(Prototype Pattern):使用原型实例...

    点击上方 Java进阶之道选择 设为星标

    优质文章,及时送达

    前言

    本文的主要内容如下:

    • 介绍原型模式

    • 示例

      • Java语言的clone

      • 浅克隆与深克隆

      • 实现深克隆

    • 原型模式的典型应用

    原型模式

    原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。

    原型模式的工作原理很简单:将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝自己来实现创建过程。

    原型模式是一种“另类”的创建型模式,创建克隆对象的工厂就是原型类自身,工厂方法由克隆方法来实现。

    需要注意的是通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址,通常对克隆所产生的对象进行修改对原型对象不会造成任何影响,每一个克隆对象都是相互独立的。通过不同的方式修改可以得到一系列相似但不完全相同的对象。

    角色

    • Prototype(抽象原型类):它是声明克隆方法的接口,是所有具体原型类的公共父类,可以是抽象类也可以是接口,甚至还可以是具体实现类。

    • ConcretePrototype(具体原型类):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。

    • Client(客户类):让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。由于客户类针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类,系统具有较好的可扩展性,增加或更换具体原型类都很方便。

    原型模式的核心在于如何实现克隆方法

    示例

    Java语言提供的clone()方法

    学过Java语言的人都知道,所有的Java类都继承自 java.lang.Object。事实上,Object 类提供一个 clone() 方法,可以将一个Java对象复制一份。因此在Java中可以直接使用 Object 提供的 clone() 方法来实现对象的克隆,Java语言中的原型模式实现很简单。

    需要注意的是能够实现克隆的Java类必须实现一个 标识接口 Cloneable,表示这个Java类支持被复制。如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个 CloneNotSupportedException 异常。

    public class Mail implements Cloneable{
    private String name;
    private String emailAddress;
    private String content;
    public Mail(){
    System.out.println("Mail Class Constructor");
    }
    // ...省略 getter、setter
    @Override
    protected Object clone() throws CloneNotSupportedException {
    System.out.println("clone mail object");
    return super.clone();
    }
    }

    在客户端创建原型对象和克隆对象也很简单,如下代码所示:

    public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
    Mail mail = new Mail();
    mail.setContent("初始化模板");
    System.out.println("初始化mail:"+mail);
    for(int i = 0;i < 3;i++){
    System.out.println();
    Mail mailTemp = (Mail) mail.clone();
    mailTemp.setName("姓名"+i);
    mailTemp.setEmailAddress("姓名"+i+"@test.com");
    mailTemp.setContent("恭喜您,此次抽奖活动中奖了");
    MailUtil.sendMail(mailTemp);
    System.out.println("克隆的mailTemp:"+mailTemp);
    }
    MailUtil.saveOriginMailRecord(mail);
    }
    }

    其中的 MailUtil 工具类为

    public class MailUtil {
    public static void sendMail(Mail mail) {
    String outputContent = "向{0}同学,邮件地址:{1},邮件内容:{2}发送邮件成功";
    System.out.println(MessageFormat.format(outputContent, mail.getName(), mail.getEmailAddress(), mail.getContent()));
    }

    public static void saveOriginMailRecord(Mail mail) {
    System.out.println("存储originMail记录,originMail:" + mail.getContent());
    }
    }

    输出如下:

    Mail Class Constructor
    初始化mail:Mail{name='null', emailAddress='null', content='初始化模板'}com.designpattern.prototype.Mail@12edcd21

    clone mail object
    向姓名0同学,邮件地址:姓名0@test.com,邮件内容:恭喜您,此次抽奖活动中奖了发送邮件成功
    克隆的mailTemp:Mail{name='姓名0', emailAddress='姓名0@test.com', content='恭喜您,此次抽奖活动中奖了'}com.designpattern.prototype.Mail@34c45dca

    clone mail object
    向姓名1同学,邮件地址:姓名1@test.com,邮件内容:恭喜您,此次抽奖活动中奖了发送邮件成功
    克隆的mailTemp:Mail{name='姓名1', emailAddress='姓名1@test.com', content='恭喜您,此次抽奖活动中奖了'}com.designpattern.prototype.Mail@52cc8049

    clone mail object
    向姓名2同学,邮件地址:姓名2@test.com,邮件内容:恭喜您,此次抽奖活动中奖了发送邮件成功
    克隆的mailTemp:Mail{name='姓名2', emailAddress='姓名2@test.com', content='恭喜您,此次抽奖活动中奖了'}com.designpattern.prototype.Mail@5b6f7412
    存储originMail记录,originMail:初始化模板

    从输出结果中我们可以观察到:

    • for循环中的 mailTemp 从 mail 对象中克隆得到,它们的内存地址均不同,说明不是同一个对象,克隆成功,克隆仅仅通过调用 super.clone() 即可。

    • 最后调用的 MailUtil.saveOriginMailRecord(mail); 中的 mail 对象的内容仍为 for 循环之前设置的内容,并没有因为克隆而改变。

    • 克隆的时候调用了 clone 方法,并没有调用 Mail 类的构造器,只在最前面 new 的时候才调用了一次

    关于输出的内存地址是怎么输出的,我们还需要看一下 Object#toString 方法

    public class Object {
    public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    //...省略...
    }

    所以所谓的内存地址即为 hashCode() 的十六进制表示,这里简单的认为 内存地址相同则为同一个对象,不同则为不同对象

    再来看一眼 Object#clone 方法

    protected native Object clone() throws CloneNotSupportedException;

    这是一个 native 关键字修饰的方法

    一般而言,Java语言中的clone()方法满足:

    • 对任何对象x,都有 x.clone() != x,即克隆对象与原型对象不是同一个对象;

    • 对任何对象x,都有 x.clone().getClass() == x.getClass(),即克隆对象与原型对象的类型一样;

    • 如果对象x的 equals() 方法定义恰当,那么 x.clone().equals(x) 应该成立。

    为了获取对象的一份拷贝,我们可以直接利用Object类的clone()方法,具体步骤如下:

    1. 在派生类中覆盖基类的 clone() 方法,并声明为public;

    2. 在派生类的 clone() 方法中,调用 super.clone()

    3. 派生类需实现Cloneable接口。

    此时,Object类相当于抽象原型类,所有实现了Cloneable接口的类相当于具体原型类

    浅克隆与深克隆

    看下面的示例

    public class Pig implements Cloneable{
    private String name;
    private Date birthday;
    // ...getter, setter, construct
    @Override
    protected Object clone() throws CloneNotSupportedException {
    Pig pig = (Pig)super.clone();
    return pig;
    }
    @Override
    public String toString() {
    return "Pig{" +
    "name='" + name + '\'' +
    ", birthday=" + birthday +
    '}'+super.toString();
    }
    }

    测试

    public class Test {
    public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Date birthday = new Date(0L);
    Pig pig1 = new Pig("佩奇",birthday);
    Pig pig2 = (Pig) pig1.clone();
    System.out.println(pig1);
    System.out.println(pig2);

    pig1.getBirthday().setTime(666666666666L);

    System.out.println(pig1);
    System.out.println(pig2);
    }
    }

    输出如下

    Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.designpattern.clone.Pig@27973e9b
    Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.designpattern.clone.Pig@312b1dae
    Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}com.designpattern.clone.Pig@27973e9b
    Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}com.designpattern.clone.Pig@312b1dae

    我们照着上一小节说的实现 Cloneable,调用 super.clone(); 进行克隆,中间我们对 pig1 对象设置了一个时间戳,从输出中我们可以发现什么问题呢?

    我们可以发现:

    • pig1 与 pig2 的内存地址不同

    • 对 pig1 设置了时间,同事 pig2 的时间也改变了

    我们通过 debug 来看一下

    6f5305b29eec71e64e96cda3a688a288.png

    debug查看对象地址

    发现如下:

    • pig1 与 pig2 地址不一样

    • pig1 的 birthday 与 pig2 的 birthday 一样

    这里引出浅拷贝与深拷贝。

    在Java语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括int、double、byte、boolean、char等简单数据类型,引用类型包括类、接口、数组等复杂类型。

    浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制,下面将对两者进行详细介绍。

    浅克隆:

    • 在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。

    • 简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制

    • 在Java语言中,通过覆盖Object类的clone()方法可以实现浅克隆。

    深克隆:

    • 在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。

    • 简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。

    • 在Java语言中,如果需要实现深克隆,可以通过序列化(Serialization)等方式来实现。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。

    实现深克隆

    方式一,手动对引用对象进行克隆:

        @Override
    protected Object clone() throws CloneNotSupportedException {
    Pig pig = (Pig)super.clone();

    //深克隆
    pig.birthday = (Date) pig.birthday.clone();
    return pig;
    }

    方式二,通过序列化的方式:

    public class Pig implements Serializable {
    private String name;
    private Date birthday;
    // ...省略 getter, setter等

    protected Object deepClone() throws CloneNotSupportedException, IOException, ClassNotFoundException {
    //将对象写入流中
    ByteArrayOutputStream bao = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bao);
    oos.writeObject(this);

    //将对象从流中取出
    ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
    ObjectInputStream ois = new ObjectInputStream(bis);
    return (ois.readObject());
    }
    }
    0886e2f3657f34f386642bee6a9b2b1e.png

    序列化方式的深克隆结果

    破坏单例模式

    饿汉式单例模式如下:

    public class HungrySingleton implements Serializable, Cloneable {

    private final static HungrySingleton hungrySingleton;

    static {
    hungrySingleton = new HungrySingleton();
    }
    private HungrySingleton() {
    if (hungrySingleton != null) {
    throw new RuntimeException("单例构造器禁止反射调用");
    }
    }
    public static HungrySingleton getInstance() {
    return hungrySingleton;
    }
    private Object readResolve() {
    return hungrySingleton;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
    return super.clone();
    }
    }

    使用反射获取对象,测试如下

    public class Test {
    public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    HungrySingleton hungrySingleton = HungrySingleton.getInstance();
    Method method = hungrySingleton.getClass().getDeclaredMethod("clone");
    method.setAccessible(true);
    HungrySingleton cloneHungrySingleton = (HungrySingleton) method.invoke(hungrySingleton);
    System.out.println(hungrySingleton);
    System.out.println(cloneHungrySingleton);
    }
    }

    输出

    com.designpattern.HungrySingleton@34c45dca
    com.designpattern.HungrySingleton@52cc8049

    可以看到,通过原型模式,我们把单例模式给破坏了,现在有两个对象了

    为了防止单例模式被破坏,我们可以:不实现 Cloneable 接口;或者把 clone 方法改为如下

        @Override
    protected Object clone() throws CloneNotSupportedException {
    return getInstance();
    }

    原型模式的典型应用

    1. Object 类中的 clone 接口

    2. Cloneable 接口的实现类,可以看到至少一千多个,找几个例子譬如:

    316905fd455c9b517b05348e0555e453.png

    Cloneable接口的实现类

    ArrayList 对 clone 的重写如下:

    public class ArrayList<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    public Object clone() {
    try {
    ArrayList> v = (ArrayList>) super.clone();
    v.elementData = Arrays.copyOf(elementData, size);
    v.modCount = 0;
    return v;
    } catch (CloneNotSupportedException e) {
    // this shouldn't happen, since we are Cloneable
    throw new InternalError(e);
    }
    }
    //...省略
    }

    调用 super.clone(); 之后把 elementData 数据 copy 了一份

    同理,我们看看 HashMap 对 clone 方法的重写:

    public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
    @Override
    public Object clone() {
    HashMap result;try {
    result = (HashMap)super.clone();
    } catch (CloneNotSupportedException e) {// this shouldn't happen, since we are Cloneablethrow new InternalError(e);
    }
    result.reinitialize();
    result.putMapEntries(this, false);return result;
    }// ...省略...
    }

    mybatis 中的 org.apache.ibatis.cache.CacheKey 对 clone 方法的重写:

    public class CacheKey implements Cloneable, Serializable {
    private List updateList;public CacheKey clone() throws CloneNotSupportedException {
    CacheKey clonedCacheKey = (CacheKey)super.clone();
    clonedCacheKey.updateList = new ArrayList(this.updateList);return clonedCacheKey;
    }// ... 省略...
    }

    这里又要注意,updateList 是 List 类型,所以可能是值类型的List,也可能是引用类型的List,克隆的结果需要注意是否为深克隆或者浅克隆

    使用原始模式的时候一定要注意为深克隆还是浅克隆。

    原型模式总结

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

    • 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。

    • 扩展性较好,由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少产品类对原有系统都没有任何影响。

    • 原型模式提供了简化的创建结构,工厂方法模式常常需要有一个与产品类等级结构相同的工厂等级结构,而原型模式就不需要这样,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品。

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

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

    • 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了“开闭原则”。

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

    适用场景:

    • 创建新对象成本较大(如初始化需要占用较长的时间,占用太多的CPU资源或网络资源),新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其成员变量稍作修改。

    • 如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原型模式配合备忘录模式来实现。

    • 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。

    -End-

    2dcb7582bd6bbbd189ffb441f6e1165b.png

    在看 2279de82e38f50058819eaa2fa872fac.png
    展开全文
  • 主要介绍了C++设计模式之原型模式,本文讲解了什么是原型模式、为什么要使用原型模式、代码实例等内容,需要的朋友可以参考下
  • 5.原型模式5.1.课程目标1、掌握原型模式和建造者模式的应用场景2、掌握原型模式的浅克隆和深克隆的写法。3、掌握建造者模式的基本写法。4、了解克隆是如何破坏单例的。5、了解原型模式的优、缺点6、掌握建造者模式和...
  • 原型模式原型模式,属于...在Java环境中,要实现原型模式,要理解对象创建、引用和克隆的相关知识,在这里通过简单分析JVM的内存在对象创建、引用和克隆时栈和堆的内容变化,来深入理解原型模式是如何在Java环境中运...
  • 设计模式-原型模式

    2021-02-08 19:51:01
    如果你想从A的实例中得到一份与A内容相同的实例,并且这两个实例互不干扰,并且实例A是运行时的状态,就需要使用原型模式原型模式与拷贝构造函数是不相同的。拷贝构造函数涉及的类是已知的, 原型模式通过指针...
  • 设计模式:原型模式

    2018-07-30 00:44:23
    本文将讲述设计模式中的原型模式,通过阅读本文你将收获如下内容: 什么是原型模式以及它的作用。 什么时间使用原型模式。 Objective-C中深拷贝和浅拷贝 原型模式的具体实践。 下面我们分条讲述。 原型模式及其...
  • 19原型模式

    千次阅读 2014-09-05 01:07:46
    1原型模式的核心内容是: A:使用一个clone方法复制自己的属性,并将自己返回 2原型模式的作用:就是一个复制自己的过程。 3原型模式具体描述 原型模式允许动态的增加或减少产品类, 产品类不需要非得...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,107
精华内容 442
关键字:

原型模式内容