一天一种JAVA设计模式之五:原型模式

写在前面的话

复习、总结23种设计模式

上一篇

# 一天一种JAVA设计模式之四:建造者模式

原型模式

定义

不通过new关键字来产生一个对象,而是通过对象复制来实现的模式就叫做原型模式

原型模式有两种表现形式:

(1)简单形式、

(2)登记形式

这两种表现形式仅仅是原型模式的不同实现。

1、简单形式的原型模式

这种形式涉及到三个角色:

客户(Client)角色:客户类提出创建对象的请求。

抽象原型(Prototype)角色:这是一个抽象角色,通常由一个Java接口或Java抽象类实现。此角色给出所有的具体原型类所需的接口。

具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象的原型角色所要求的接口。

java 复制代码
package com.design.pattern.prototype.test04;  
  
// 抽象原型角色  
public interface Prototype {  
    /**  
    * 克隆自身的方法  
    *  
    * @return 一个从自身克隆出来的对象  
    */  
    public Object clone();  
    }  

    // 具体原型角色  
    class ConcretePrototype1 implements Prototype {  
        public Prototype clone() {  
            //最简单的克隆,新建一个自身对象,由于没有属性就不再复制值了  
            Prototype prototype = new ConcretePrototype1();  
            return prototype;  
        }  
    }  

    class ConcretePrototype2 implements Prototype {  
        public Prototype clone() {  
            //最简单的克隆,新建一个自身对象,由于没有属性就不再复制值了  
            Prototype prototype = new ConcretePrototype2();  
            return prototype;  
        }  
    }
java 复制代码
package com.design.pattern.prototype.test04;  
  
// 客户端角色  
public class Client {  
    /**  
    * 持有需要使用的原型接口对象  
    */  
    private Prototype prototype;  

    /**  
    * 构造方法,传入需要使用的原型接口对象  
    */  
    public Client(Prototype prototype) {  
        this.prototype = prototype;  
    }  

    public void operation(Prototype example) {  
        //需要创建原型接口的对象  
        Prototype copyPrototype = (Prototype) prototype.clone();  
        System.out.println(copyPrototype);  
    }  
}

2、登记形式的原型模式

作为原型模式的第二种形式,它多了一个原型管理器(PrototypeManager)角色,

该角色的作用是:创建具体原型类的对象,并记录每一个被创建的对象。

3.两种形式的比较

简单形式和登记形式的原型模式各有其长处和短处。

如果需要创建的原型对象数目较少而且比较固定的话,可以采取第一种形式。在这种情况下,原型对象的引用可以由客户端自己保存。

如果要创建的原型对象数目不固定的话,可以采取第二种形式。在这种情况下,客户端不保存对原型对象的引用,这个任务被交给管理员对象。在复制一个原型对象之前,客户端可以查看管理员对象是否已经有一个满足要求的原型对象。如果有,可以直接从管理员类取得这个对象引用;如果没有,客户端就需要自行复制此原型对象。

构造函数不会被执行

java 复制代码
package com.design.pattern.prototype.test01;  
  
    /*  
    如果Things没有实现java.lang.Cloneable接口,  
    在调用super.clone()时会抛出CloneNotSupportedException  
    */  
    public class Things implements Cloneable {  

    public Things() {  
        System.out.println("构造函数被执行...");  
    }  

    @Override  
    protected Object clone() throws CloneNotSupportedException {  
        // 如果Things没有实现java.lang.Cloneable接口,在调用super.clone()时会抛出CloneNotSupportedException  
        return super.clone();  
    }  
  
}

Object类的clone()的原理是从内存中以二进制流的方式进行拷贝,重新分配一个内存块,那构造函数没有被执行也是非常正常的了

java 复制代码
package com.design.pattern.prototype.test01;  
  
/**  
Object类的clone()的原理是从内存中以二进制流的方式进行拷贝,  
重新分配一个内存块,那构造函数没有被执行也是非常正常的了  
*/  
public class Test01构造函数不会被执行 {  

    public static void main(String[] args) {  
        Things things = new Things();  
        try {  
            Object clone = things.clone();  
            System.out.println(things);  
            System.out.println(clone);  
            System.out.println(clone instanceof Things);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}

原型模式实现

原型模式的克隆分为浅克隆和深克隆

浅拷贝

Object类的clone()只是拷贝本对象,其内部对象的数组,引用对象都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝

java 复制代码
package com.design.pattern.prototype.test02;  

import java.util.ArrayList;  
import java.util.List;  
  
public class Person implements Cloneable{  

    private List<String> value = new ArrayList<String>();  

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

    public void addValue(String v) {  
        this.value.add(v);  
    }  

    public List<String> getValue() {  
        return this.value;  
    }  
  
}
java 复制代码
package com.design.pattern.prototype.test02;  
  
import java.util.Arrays;  
  
/*  
Object类的clone()只是拷贝本对象,其内部对象的数组,  
引用对象都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝  
*/  
public class Test02浅拷贝 {  

    public static void main(String[] args) {  
        Person person = new Person();  
        person.addValue("123");  

        Person newPerson = person.clone();  
        newPerson.addValue("456");  

        System.out.println(person);  
        System.out.println(newPerson);  

        // [123, 456]  
        System.out.println(Arrays.toString(person.getValue().toArray(new String[0])));  
        // [123, 456]  
        System.out.println(Arrays.toString(newPerson.getValue().toArray(new String[0])));  
   }  
  
}

深拷贝

深度克隆要深入到多少层,是一个不易确定的问题。在决定以深度克隆的方式复制一个对象的时候,必须决定对间接复制的对象时采取浅度克隆还是继续采用深度克隆。因此,在采取深度克隆时,需要决定多深才算深。此外,在深度克隆的过程中,很可能会出现循环引用的问题,必须小心处理。

java 复制代码
package com.design.pattern.prototype.test03;  
  
import java.util.ArrayList;  
import java.util.List;  
  
public class Person implements Cloneable{  

    private ArrayList<String> value = new ArrayList<String>();  

    @Override  
    protected Person clone() {  
        try {  
            Person clone = (Person) super.clone();  
            clone.value = (ArrayList<String>) this.value.clone();  
            return clone;  
        } catch (CloneNotSupportedException e) {  
            e.printStackTrace();  
        }  
        return null;  
    }  

    public void addValue(String v) {  
        this.value.add(v);  
    }  

    public List<String> getValue() {  
        return this.value;  
    }  
  
}
java 复制代码
package com.design.pattern.prototype.test03;  
  
import java.util.Arrays;  
  
public class Test03深拷贝 {  
  
    public static void main(String[] args) {  
        Person person = new Person();  
        person.addValue("123");  

        Person newPerson = person.clone();  
        newPerson.addValue("456");  

        System.out.println(person);  
        System.out.println(newPerson);  

        // [123]  
        System.out.println(Arrays.toString(person.getValue().toArray(new String[0])));  
        // [123, 456]  
        System.out.println(Arrays.toString(newPerson.getValue().toArray(new String[0])));  
    }  
}

浅拷贝与深拷贝的区别

深拷贝:在对类中引用数据类型(类的实例对象)进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量

浅拷贝:只对类中基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象

浅拷贝和深拷贝是相对的,如果一个对象内部只有基本数据类型,那用 clone() 方法获取到的就是这个对象的深拷贝,而如果其内部还有引用数据类型,那用 clone() 方法就是一次浅拷贝的操作

下一篇

# 一天一种JAVA设计模式之四:建造者模式

相关推荐
【D'accumulation】21 分钟前
令牌主动失效机制范例(利用redis)注释分析
java·spring boot·redis·后端
2401_8543910831 分钟前
高效开发:SpringBoot网上租赁系统实现细节
java·spring boot·后端
Cikiss39 分钟前
微服务实战——SpringCache 整合 Redis
java·redis·后端·微服务
Cikiss41 分钟前
微服务实战——平台属性
java·数据库·后端·微服务
OEC小胖胖1 小时前
Spring Boot + MyBatis 项目中常用注解详解(万字长篇解读)
java·spring boot·后端·spring·mybatis·web
2401_857617621 小时前
SpringBoot校园资料平台:开发与部署指南
java·spring boot·后端
计算机学姐1 小时前
基于SpringBoot+Vue的在线投票系统
java·vue.js·spring boot·后端·学习·intellij-idea·mybatis
Yvemil72 小时前
MQ 架构设计原理与消息中间件详解(二)
开发语言·后端·ruby
2401_854391083 小时前
Spring Boot大学生就业招聘系统的开发与部署
java·spring boot·后端
虽千万人 吾往矣3 小时前
golang gorm
开发语言·数据库·后端·tcp/ip·golang