目录
一、什么是原型模式
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象,称之为原型模式(Prototype模式)
二、示例程序
1、Product
package com.as.module.prototype;
/**
* 产品
* @author Andy
* @date 2021/4/25 22:11
*/
public interface Product extends Cloneable{
public abstract void use(String s);
public abstract Product createClone();
}
2、Manager类
package com.as.module.prototype;
import java.util.HashMap;
/**
* TODO
* @author Andy
* @date 2021/4/25 22:12
*/
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();
}
}
3、MessageBox类
package com.as.module.prototype;
/**
* TODO
*
* @author Andy
* @date 2021/4/25 22:31
*/
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("");
}
@Override
public Product createClone() {
Product p = null;
try {
p = (Product) clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
4、UnderlinePen类
package com.as.module.prototype;
/**
* TODO
*
* @author Andy
* @date 2021/4/25 22:37
*/
public class UnderlinePen implements Product{
private char ulchar;
public UnderlinePen(char ulchar){
this.ulchar = ulchar;
}
@Override
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("");
}
@Override
public Product createClone() {
Product p = null;
try {
p = (Product) clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
5、TestPrototype类
package com.as.module.prototype;
/**
* TODO
*
* @author Andy
* @date 2021/4/25 22:40
*/
public class TestPrototype {
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.");
}
}
6、运行结果
三、UML图
登场角色
1、Prototype(原型)
Product角色负责定义用于复制现有实例来生成新实例的方法。在示例程序中,由Product接口扮演此角色
2、ConcretePrototype(具体的原型)
ConcretePrototype角色负责实现复制现有实例并生成新实例的方法。在示例程序中,由MessageBox类和UnderlinePen类扮演此角色。
3、Client(使用者)
Client角色负责使用复制实例的方法生成新的实例。在示例程序中,由Manager类扮演此角色
四、拓展思路与项目实战
1、不能根据类来生成实例嘛
大家可能都有这样的疑惑,既然是创建类,为什么不能直接用new Something() 呢?
(1)对象种类繁多,无法将他们整合到一个类中
在示例程序中,一共出现3种样式:
使用'~'为字符串添加下划线
使用'*'为字符串添加边框
使用'/'为字符串添加边框
但是如果将每种样式都编写为一个类,类的数量将会非常庞大,源程序的管理也会变得非常困难
(2)难以根据类生成实例时
当我们想生成复杂实例时,根据实例来生成实例要比根据类来生成实例要复杂的多
(3)想解耦框架与生成的实例时
示例程序中,我们将复制(clone)实例的部分封装在frameworkwork包中了。
在Manager类的create方法中,我们并没有使用类名,取而代之的是使用了“string message”,和"slash box"等字符串为生成的实例命名。与Java语言自带的生成实例的new something()方式相比,这种方式具有很好的通用性,而且将框架从类名的束缚中解脱出来了。
2、类名是束缚嘛
在源程序中使用类名到底会有什么问题呢?在代码中出现要使用的类的名字不是理所当然的吗?但是面对对象编程的目标之一,就是“作为组件复用”。在代码中出现要使用的类的名字并非总是坏事。不过,一旦在代码中出现要使用的类的名字,就无法与该类分离开来,也就无法实现复用。
当然,可以通过替换源代码或是改变类名来解决这个问题。但是,此处说的“作为组件复用”中不包含替换源代码。以Java来说,重要的是当手边只有class文件(.class)时,该类能否被复用。即使没有Java文件也能复用该类才是关键
当多个类必须紧密结合时,代码中出现这些类的名字是没有问题的,但是如果那些需要被独立出来作为组件复用的类的名字出现在代码中,那就有问题了。
3、原型模式的优点
(1)性能优良
原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量对象时,原型模式可以很好的体现其优点
(2)逃避构造函数的约束
这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的。优点就是减少了约束,缺点也是减少了约束,需要在实际应用中考虑。
4、原型模式的使用场景
(1)资源优化场景
类初始化需要消耗非常多的资源,这个资源包括数据,硬件资源等。
(2)性能和安全要求的场景
通过new产生一个对象需要非常繁琐的数据准备和访问权限,则可以使用原型模式
(3)一个对象多个修改者的场景
一个对象需要提供给其他对象访问,而且各个调用者可能需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。