通常通过new关键字指定类名来生成类的对象实例,但是如果初始化一个类需要消耗非常多的资源,或者是要处理的对象太多了,过程十分复杂的情况下,就不太方便了,性能和安全性也不太好
Prototype原型模式,通过一个原型对象来指明所要创建的对象类型,然后复制这个原型对象来创建出更多同类型的对象
实现Cloneable接口:Cloneable接口的作用是在运行时通知JVM可以安全地在已经实现了此接口的类上使用clone方法,只有实现了这个接口的类才能被拷贝,否则运行时会抛出CloneNotSupportedExcepton异常
重写clone方法:Object类中有一个clone方法,返回对象的一个拷贝,但是作用域是protected类型,一般的类无法调用,因此需要将clone方法的作用域修改为public类型
下面的例子,将输入的字符串加上下划线,以及字符串放到四周为*和/的方框包围起来
这里单独创建一个框架prototype.framework包,主要负责复制对象实例
Product类:声明了抽象方法use和createClone的接口(prototype.framework包)
Manager类:调用createClone方法复制实例的类(prototype.framework包)
MessageBox类:将字符串放入方框中并显示的类;实现了use和createClone方法
UnderlinePen类:将字符串加上下划线并显示的类;实现了use和createClone方法
Main类:main,测试
Product接口和Manager类负责来复制实例,虽然Manager类会调用createClone方法,但是对于具体要复制哪个类一无所知;但是,只要是实现了Product接口的类,调用它的createClone方法,就可以复制出新的实例了,这段话就是实现,得理解
clone()的源码说明:
* The class {@code Object} does not itself implement the interface
* {@code Cloneable}, so calling the {@code clone} method on an object
* whose class is {@code Object} will result in throwing an
* exception at run time.
*
* @return a clone of this instance.
* @throws CloneNotSupportedException if the object's class does not
* support the {@code Cloneable} interface. Subclasses
* that override the {@code clone} method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see java.lang.Cloneable
*/
protected native Object clone() throws CloneNotSupportedException;
首先是Product接口,它是复制功能的接口,继承Cloneable接口,实现了这个接口的类的实例就可以调用clone方法自动复制实例,use给子类实现具体的方法,createClone方法是用于复制实例的方法
package prototype.framework;
/**
* Copyright (C), 2014-2018, maoxiaomeng.com
* FileName: Product
* Author: lihui
* Date: 2018/5/2 21:58
*/
public interface Product extends Cloneable {
public abstract void use(String s);
public abstract Product createClone();
}
然后Manager类,使用Product接口来复制实例
定义一个HashMap类型showcase,保存了名字:实例之间的映射关系
register方法,将收到的一组映射关系注册到showcase中,第二个参数Product proto具体的类型还不知道,只知道它实现了Product接口的类的实例,因此它是可以调用Product接口里定义的两个抽象方法
可以看到,prototype.framework包里这Product接口和Manager类中没有出现其它类,那么可以独立解耦地修改,不受其它类的影响;Manager类里只有Product接口名,这个接口也就是Manager类和其它类连接的桥梁
package prototype.framework;
import java.util.HashMap;
/**
* Copyright (C), 2014-2018, maoxiaomeng.com
* FileName: Manager
* Author: lihui
* Date: 2018/5/2 21:58
*/
public class Manager {
private HashMap showcase = new HashMap();
public void register(String name, Product proto) {
showcase.put(name, proto);
}
public Product create(String protoName) {
Product p = (Product)showcase.get(protoName);
return p.createClone();
}
}
接着是MessageBox类
decochar字段保存字符串四周是用什么符号框起来
use方法使用decochar保存的字符将字符串框起来
createClone方法用于复制自己,主要是调用clone方法;在进行复制时,原来实例中的字段的值也是会被复制到新的实例中;根据上面源码的说明,之所以能够调用clone方法进行复制,因为该类实现了Cloneable接口,如果没有实现这个接口,会抛出CloneNotSupportException异常,因此需要try捕获
这里MessageBox类是实现了Product接口,但是Product接口是继承Cloneable接口的,因此这里实现没问题,不会抛出异常
clone方法只有类或者子类进行调用,当要复制实例,必须先调用createClone方法,然后方法内部调用clone方法
package prototype;
import prototype.framework.Product;
/**
* Copyright (C), 2014-2018, maoxiaomeng.com
* FileName: MessageBox
* Author: lihui
* Date: 2018/5/2 22:03
*/
public class MessageBox implements Product {
private char decochar;
public MessageBox(char decochar) {
this.decochar = decochar;
}
public void use(String s) {
int length = s.getBytes().length;
for (int i = 0; i < length + 4; i++) {
System.out.print(decochar);
}
System.out.println("");
System.out.println(decochar + " " + s + " " + decochar);
for (int i = 0; i < length + 4; i++) {
System.out.print(decochar);
}
System.out.println("");
}
public Product createClone() {
Product p = null;
try {
p = (Product)clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
同时,Cloneable接口作用只是告诉程序可以调用clone方法,自身没有定义任何方法
* @author unascribed
* @see java.lang.CloneNotSupportedException
* @see java.lang.Object#clone()
* @since JDK1.0
*/
public interface Cloneable {
}
然后UnderlinePen类,和上面MessageBox一样,只不过字符串的处理不同
package prototype;
import prototype.framework.Product;
/**
* Copyright (C), 2014-2018, maoxiaomeng.com
* FileName: UnderlinePen
* Author: lihui
* Date: 2018/5/2 21:57
*/
public class UnderlinePen implements Product {
private char ulchar;
public UnderlinePen(char ulchar) {
this.ulchar = ulchar;
}
public void use(String s) {
int length = s.getBytes().length;
System.out.println("\"" + s + "\"");
System.out.print(" ");
for (int i = 0; i < length; i++) {
System.out.print(ulchar);
}
System.out.println("");
}
public Product createClone() {
Product p = null;
try {
p = (Product)clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
最后是main,创建Manager实例,实例里注册了UnderLinePen实例和MessageBox实例
“strong message”:UnderlinePen实例,ulchar为’~’
“warning box”:MessageBox实例,decochar为’*’
“slash box”:MessageBox实例,decochar为’/’
这就是注册内容
package prototype;
import prototype.framework.Manager;
import prototype.framework.Product;
/**
* Copyright (C), 2014-2018, maoxiaomeng.com
* FileName: Main
* Author: lihui
* Date: 2018/5/2 21:57
*/
public class Main {
public static void main(String[] args) {
Manager manager = new Manager();
UnderlinePen upen = new UnderlinePen('~');
MessageBox mbox = new MessageBox('*');
MessageBox sbox = new MessageBox('/');
manager.register("strong message", upen);
manager.register("warning box", mbox);
manager.register("slash box", sbox);
Product p1 = manager.create("strong message");
p1.use("Hello, world");
Product p2 = manager.create("warning box");
p2.use("Hello, world");
Product p3 = manager.create("slash box");
p3.use("Hello, world");
}
}
Prototype原型模式里有下列角色
Prototype:原型,例子当中的Product;负责定义用于复制现有实例生成新实例的方法
ConcretePrototype:具体的原型,例子当中的MessageBox和UnderlinePen类;负责实现复制现有实例并生成新实例的方法
Client:使用者,例子当中的Manager类;负责使用复制实例的方法生成新的实例
使用原型模式,就不需要用new来进行初始化类生成实例,可以看到,复制实例的过程都封装到了framework包里,而且在create方法中,传入的都是注册好了的实例的命名,而不需要使用类名