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

示例程序
将字符串放到框里面显示,或者加上下划线显示
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)的深拷贝和浅拷贝
浅拷贝的介绍
- 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
- 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
- 前面我们克隆羊就是浅拷贝
- 浅拷贝是使用默认的 clone()方法来实现
深拷贝基本介绍
- 复制对象的所有基本数据类型的成员变量值
- 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝
- 深拷贝实现方式1:重写clone方法来实现深拷贝
- 深拷贝实现方式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)的注意事项和细节
- 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
- 不用重新初始化对象,而是动态地获得对象运行时的状态
- 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
- 在实现深克隆的时候可能需要比较复杂的代码
- 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则
留言