目录
一、简介
1、定义
原型模式就是从一个对象再创建另一个可定制的对象,而不需要知道任何创建的细节。
2、使用场景
我们现在一般会使用new关键字指定类名生成类的实例(PS:我们以前使用java.lang.Cloneable的一个很大原因是使用new创建对象的速度相对来说会慢一些,随着JVM性能的提升,new的速度和Object的clone()方法的速度差不多了。)。
使用new关键字创建类的时候必须指定类名,但是在开发过程中也会有“在不指定类名的前提下生成实例”的需求。例如,在下面这些情况下,就需要根据现有的实例来生成新的实例。
1) 对象种类繁多,无法将他们整合到一个类的时候;
2) 难以根据类生成实例时;
3) 想解耦框架与生成的实例时。
如果想要让生成实例的框架不再依赖于具体的类,这时,不能指定类名来生成实例,而要事先“注册”一个“原型”实例,然后通过复制该实例来生成新的实例。
3、模式分析
在原型模式结构中定义了一个抽象原型类,所有的Java类都继承自java.lang.Object,而Object类提供一个clone()方法,可以将一个Java对象复制一份。因此在Java中可以直接使用Object提供的clone()方法来实现对象的克隆,Java语言中的原型模式实现很简单。
能够实现克隆的Java类必须实现一个标识接口Cloneable,表示这个Java类支持复制。如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个CloneNotSupportedException异常。
注意: `java.lang.Cloneable 只是起到告诉程序可以调用clone方法的作用,它本身并没有定义任何方法。
在使用原型模式克隆对象时,根据其成员对象是否也克隆,原型模式可以分为两种形式:深克隆 和 浅克隆 。
4、优缺点
原型模式的优点:
- 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过一个已有实例可以提高新实例的创建效率。
- 可以动态增加或减少产品类。
- 原型模式提供了简化的创建结构。
- 可以使用深克隆的方式保存对象的状态。
原型模式的缺点:
- 需要为每一个类配备一个克隆方法,而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事,必须修改其源代码,违背了“开闭原则”。
- 在实现深克隆时需要编写较为复杂的代码。
二、实现
1、UML类图
2、Prototype
package com.mfc.design.原型模式;
/**
* @author MouFangCai
* @date 2019/10/12 14:41
*/
public abstract class Prototype implements Cloneable {
private String id;
protected String type;
abstract void draw();
public String getType(){
return type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
3、ConcretePrototypeA
package com.mfc.design.原型模式;
/**
* @author MouFangCai
* @date 2019/10/12 14:45
*/
public class ConcretePrototypeA extends Prototype {
@Override
void draw() {
System.out.println("这是ConcretePrototypeA的draw()");
}
public ConcretePrototypeA(){
type = "ConcretePrototypeA";
}
}
4、ConcretePrototypeB
package com.mfc.design.原型模式;
/**
* @author MouFangCai
* @date 2019/10/12 14:45
*/
public class ConcretePrototypeB extends Prototype {
@Override
void draw() {
System.out.println("这是ConcretePrototypeB的draw()");
}
public ConcretePrototypeB(){
type = "ConcretePrototypeB";
}
}
5、PrototypeCache
package com.mfc.design.原型模式;
import java.util.Hashtable;
/**
* @author MouFangCai
* @date 2019/10/12 14:45
*/
public class PrototypeCache {
// 维护一个注册表
private static Hashtable<String, Prototype> prototypeMap = new Hashtable<>();
// 提供一个获取新实例的方法
public static Prototype getprototype(String prototypeId) {
Prototype cached = prototypeMap.get(prototypeId);
return (Prototype) cached.clone();
}
// 对每种形状都运行数据库查询,并创建该形状
// prototypeMap.put(prototypeId, Prototype);
// 例如,我们要添加2种Prototype
public static void loadCache() {
ConcretePrototypeA A = new ConcretePrototypeA();
A.setId("1");
prototypeMap.put(A.getId(),A);
ConcretePrototypeB B = new ConcretePrototypeB();
B.setId("2");
prototypeMap.put(B.getId(),B);
}
}
6、Client_Prototype
package com.mfc.design.原型模式;
/**
* @author MouFangCai
* @date 2019/10/12 15:00
*/
public class Client_Prototype {
public static void main(String[] args) {
PrototypeCache.loadCache();
Prototype clonedPrototype1 = (Prototype) PrototypeCache.getprototype("1");
System.out.println("Prototype : " + clonedPrototype1.getType());
Prototype clonedPrototype2 = (Prototype) PrototypeCache.getprototype("2");
System.out.println("Prototype : " + clonedPrototype2.getType());
}
}
三、原型模式分析
上面这个例子,参考自菜鸟教程:https://www.runoob.com/design-pattern/prototype-pattern.html(小编不是很理解这个原型模式的例子,感觉似乎没什么实际意义,只是new对象的操作换了个地方而已)
从上面这个例子分析,总感觉原型模式似乎有些问题。所以小编再次参考阅读了《大话设计模式》,详情请见下面的例子
1、问题描述
比如说,有一个使用场景需要不同的Person对象,但是其name属性一样,但是age和score属性需要根据环境不同而设置不同的值
2、Person_Prototype
package com.mfc.design.原型模式2;
/**
* @author MouFangCai
* @date 2019/10/12 16:12
*/
// 原型Prototype
public class Person_Prototype implements Cloneable {
private String name;
private int age;
private int score;
// 设置需要复制的属性(公有属性)
public Person_Prototype(String name){
this.name = name;
}
// 设置需要自定义的属性
public void setMessage(int age, int score){
this.age =age;
this.score =score;
}
public void dispaly(){
System.out.println("name:" + name + "----age:"+ age + "----score:" + score);
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
3、Cilent客户端
package com.mfc.design.原型模式2;
/**
* @author MouFangCai
* @date 2019/10/12 15:00
*/
public class Client_Prototype {
public static void main(String[] args) throws CloneNotSupportedException {
Person_Prototype p1 = new Person_Prototype("mfc");
p1.setMessage(18, 22);
// 复制一个新的Person,并修改对应的细节属性
Person_Prototype p2 = (Person_Prototype)p1.clone();
p2.setMessage(24,44 );
System.out.println("是否是同一个对象:" + (p1 == p2));
p1.dispaly();
p2.dispaly();
}
}
结果如下:
是否是同一个对象:false
name:mfc----age:18----score:22
name:mfc----age:24----score:44Process finished with exit code 0
4、分析
从运行结果,我们可以看到,Person对象被复制了一个,同时name属性保持一致,age被重新自定义,达到我们预期要求
备注:这只是 浅复制,如果Person对象有一个私有属性是对象时,就需要使用 深复制 进行实现,有兴趣者可自行百度。