根据实例原型,实例模型来生成实例


示例程序

将字符串放到框里面显示,或者加上下划线显示

UML类图

示例程序中的所有类和接口

名字说明
Product接口复制功能的接口,继承了Cloneable接口
Manager类使用Product接口来复制实例
MessageBox类将字符串用框的格式显示出来,实现了Product接口
UnderlinePan类将字符串用下划线的格式显示出来,实现了Product接口
Main类测试类

1. Product接口

package Prototype;

/**
 * 复制功能的接口,继承了java.lang.Cloneable接口
 * @author Moti
 * @Time 2019年9月19日
 */
public interface Product extends Cloneable {

    /**
     * 用于使用的方法,具体怎么使用要交给子类去处理
     * 
     * @param s
     */
    public abstract void user(String s);

    /**
     * 用于复制实例
     * @return
     */
    public abstract Product creatClone();
} 

2. Manager类

package Prototype;

import java.util.HashMap;

/**
 * 使用Product接口来复制实例
 * 
 * @author Moti
 * @Time 2019年9月19日
 */
public class Manager {

    private HashMap showCase = new HashMap();

    /**
     * 接受一组名字和Product注册到showCase中.
     * 
     * @param name
     * @param product
     */
    public void register(String name, Product product) {
        showCase.put(name, product);
    }

    /**
     * 根据名字获得复制后的实例
     * 
     * @param name
     * @return
     */
    public Product create(String name) {
        Product p = (Product) showCase.get(name);
        return p.creatClone();
    }
} 

3. MessageBox类

package Prototype;

/**
 * @author Moti
 * @Time 2019年9月19日
 */
public class MessageBox implements Product {
    //修饰边框的字符
    private char c;
    public MessageBox(char c) {
        this.c = c;
    }
    /**
     * 显示修饰之后的字符串
     */
    @Override
    public void user(String s) {
        int length = s.getBytes().length;
        for (int i = 0; i < length + 4; i++) {
            System.out.print(c);
        }
        System.out.println();
        System.out.println(c+" "+s+" "+c);
        for (int i = 0; i < length + 4; i++) {
            System.out.print(c);
        }
        System.out.println();
    }

    /**
     * 复制当前类的实例
     */
    @Override
    public Product creatClone() {
        Product p = null;
        try {
            //注意:只有类自己(或者是子类)能够调用clone方法,
            //当其他类要求复制实例时,必须先调用createClone这样的方法,然后在该方法内部再调用clone方法
            p = (Product) clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }

} 

4. UnderlinePan类

package Prototype;

/**
 * @author Moti
 * @Time 2019年9月19日
 */
public class UnderlinePan implements Product {
    //修饰边框的字符
    private char c;
    public UnderlinePan(char c) {
        this.c = c;
    }
    @Override
    public void user(String s) {
        int length = s.getBytes().length;
        System.out.println("\""+s+"\"");
        System.out.println();
        for (int i = 0; i < length; i++) {
            System.out.print(c);
        }
        System.out.println();
    }

    @Override
    public Product creatClone() {
        Product p = null;
        try {
            //注意:只有类自己(或者是子类)能够调用clone方法,
            //当其他类要求复制实例时,必须先调用createClone这样的方法,然后在该方法内部再调用clone方法
            p = (Product) clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }

} 

5. Main类

package Prototype;

/**
 * @author Moti
 * @Time 2019年9月19日
 */
public class Main {
    public static void main(String[] args) {
        Manager m = new Manager();
        UnderlinePan underlinePan = new UnderlinePan('~');
        MessageBox messageBox = new MessageBox('*');
        m.register("underlinePan", underlinePan);
        m.register("messageBox", messageBox);

        //通过复制生成实例
        Product p1 = m.create("messageBox");
        Product p2 = m.create("messageBox");
        p1.user("Hello World");
        System.out.println("通过复制生成的对象是同一个吗:"+(p1 == p2));

        Product p3 = m.create("underlinePan");
        Product p4 = m.create("underlinePan");
        p4.user("Hello World");
        System.out.println("通过复制生成的对象是同一个吗:"+(p3 == p4));
    }
} 

运行结果

由结果知道,通过复制来获得实例,每次获得的都是新的实例

登场的角色

  • Prototype(原型)
    负责定义用于复制现有的实例来生成新的实例的方法.示例程序中,由Product接口扮演这个角色
  • ConcretePrototype(具体的原型)
    负责实现复制现有的实例并生成新的实例的方法.示例程序中,MessageBox和UnderlinePan扮演这个角色
  • Client(使用者)
    负责使用复制实例的方法来生成新的实例.Manager扮演这个角色.

原型模式(Prototype)在Spring框架中的应用(源码分析)

Spring中原型bean的创建,就是原型模式的应用

1. beans.xml
<bean id="one" class="com.bean.entity" scope="prototype"/>
2. Test.java
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//获取monster[通过id获取monster]
Object bean = applicationContext.getBean("one");
System.out.println("bean" + bean); 
3. Spring源码
4. Spring源码

原型模式(Prototype)的深拷贝和浅拷贝

浅拷贝的介绍

  1. 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
  2. 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
  3. 前面我们克隆羊就是浅拷贝
  4. 浅拷贝是使用默认的 clone()方法来实现

深拷贝基本介绍

  1. 复制对象的所有基本数据类型的成员变量值
  2. 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝
  3. 深拷贝实现方式1:重写clone方法来实现深拷贝
  4. 深拷贝实现方式2:通过对象序列化实现深拷贝(推荐)实现深拷贝的两种方法代码实现
DeepCloneableTarget类

本类的实例对象会成为其他类的成员变量,其他的类想要实现深拷贝,那么这个实例的对象也要复制,所以这个类也需要重写clone方法

package Prototype.deepclone;

import java.io.Serializable;

public class DeepCloneableTarget implements Serializable, Cloneable {

    private String cloneName;

    // 构造器
    public DeepCloneableTarget(String cloneName) {
        this.cloneName = cloneName;
    }

    // 因为该类的属性,都是String , 因此我们这里使用默认的clone完成即可
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
} 
DeepProtoType类
package Prototype.deepclone;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class DeepProtoType implements Serializable, Cloneable {

    public String name; // String 属性
    public DeepCloneableTarget deepCloneableTarget;// 引用类型

    public DeepProtoType() {
        super();
    }

    // 深拷贝 - 方式 1 使用clone 方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object deep = null;
        // 这里完成对基本数据类型(属性)和String的克隆
        deep = super.clone();
        // 对引用类型的属性,进行单独处理,如果有多个引用类型的属性,则需要在这里分开处理
        DeepProtoType deepProtoType = (DeepProtoType) deep;
        deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();
        return deepProtoType;
    }

    // 深拷贝 - 方式2 通过对象的序列化实现 (推荐)
    public Object deepClone() {
        // 创建流对象
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            // 序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this); // 当前这个对象以对象流的方式输出
            // 反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            DeepProtoType copyObj = (DeepProtoType) ois.readObject();
            return copyObj;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            // 关闭流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (Exception e2) {
                System.out.println(e2.getMessage());
            }
        }
    }
} 

Client类

package Prototype.deepclone;

public class Client {

    public static void main(String[] args) throws Exception {
        DeepProtoType p = new DeepProtoType();
        p.name = "宋江";
        p.deepCloneableTarget = new DeepCloneableTarget("大牛");

        // 方式1 完成深拷贝
//        DeepProtoType p2 = (DeepProtoType) p.clone();
//        System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
//        System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());

        // 方式2 完成深拷贝
        DeepProtoType p2 = (DeepProtoType) p.deepClone();
        System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
        System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());
    }
} 

运行结果

原型模式(Prototype)的注意事项和细节

  1. 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
  2. 不用重新初始化对象,而是动态地获得对象运行时的状态
  3. 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
  4. 在实现深克隆的时候可能需要比较复杂的代码
  5. 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则
最后修改日期:2020-07-13

作者

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。