原型模式 prototype 创建型 设计模式(七)
原型模式 prototype
意图
用原型实例指定需要创建的对象的类型,然后使用复制这个原型对象的方法创建出更多同类型的对象 显然,原型模式就是给出一个对象,然后克隆一个或者更多个对象 小时候看过的动画片《西游记》,主题曲猴哥中有一句“拔一根毫毛 ,吹出猴万个 ”这就是原型模式 孙悟空作为原型对象,“拔一根毫毛 ,吹” 这就是调用复制对象方法,“猴”万个,就是结果了,创建了“万个” 猴子原型模式的根本-拷贝
原型模式的根本在于对象的拷贝 说白了就是:如何复制一个对象? 对象的表示 Object ref = new Object(); 上面这句话可以理解为三个步骤- 创建一个Object类型的引用名为 ref
- 创建了一个Object类型的对象
- 使变量ref指向这个新的对象的内存地址
结构
原型模式作为创建型模式的一种 与工厂模式建造者模式是类似的,都是为了创建对象 只不过是创建的方式不同 原型模式的创建逻辑则是借助于已经存在的对象,调用他的拷贝方法,从而创建相同类型的新的对象 根据依赖倒置原则,面向抽象而不是细节进行编程,所以使用抽象角色Prototype用于描述原型类 一种通用的结构描述形式为: Client 客户端角色 客户端程序发起创建对象请求 Prototype 抽象原型角色 抽象角色用于描述原型类,给出具体原型类需要的协议 接口或者抽象类 ConcretePrototype具体原型角色 被复制的对象类型代码示例
package prototype; public interface Prototype extends Cloneable { Prototype clone(); }
package prototype; public class ConcreatePrototype implements Prototype { @Override public Prototype clone() { try{ return (Prototype)super.clone(); }catch (CloneNotSupportedException e){ return null; } } }
package prototype; public class Client { public static void main(String[] args){ Prototype prototype = new ConcreatePrototype(); Prototype clonedObj = (Prototype)prototype.clone(); System.out.println(clonedObj.getClass()); System.out.println(prototype == clonedObj); } }
Java天然的原型模式
在Java中,所有的对象都继承自Java.lang.Object 而 Object中有clone()方法 ,可以将一个对象进行拷贝 所以说Java天生的内置了原型模式---通过对象的clone方法进行对象的拷贝 不过也有一些具体的规则需要注意 Java语言提供了Cloneable接口,作为标记接口 凡是实现了Cloneable接口的类都声称:“可以安全的在这个类上使用clone()方法”。 试图调用clone()方法时,如果此对象的类没有实现 Cloneable 接口,则会抛出 CloneNotSupportedException。 clone()方法如下 clone方法是浅拷贝而不是深拷贝 Object中的clone()方法规定了拷贝的一般协议,可以参看API文档- 对于任何对象 x,表达式:x.clone() != x,克隆对象与原始对象不是同一个对象
- x.clone().getClass() == x.getClass() ,克隆对象与原始对象是同一种类型
- x.clone().equals(x) 为true
- 实现Cloneable接口
- 覆盖或者使用继承而来的clone()方法
拥有管理器的原型模式
原型模式中最为关键的是调用某个对象的拷贝方法,进行原始对象的复制 所以原型模式一种常见的用法就是借助于这个"原始对象",达到工厂方法的效果 客户端中保存一个ConcretePrototype类型的对象 后续的对象创建获取就是客户端通过这个内部的对象,调用它的拷贝方法进行进一步的操作 如果产品结构比较简单,可能只需要几种类型的对象即可 上面的原型结构比较适合,客户端自己保存所有的对象 但是 如果产品等级结构比较杂乱,或者说要创建的原型对象是数目并不是固定的 又可以进一步将管理对象的职责进行提取分离,抽象出来一个管理器的角色 专门用于管理这些对象 这种带管理器的原型模式中,客户端就不在持有原型对象的引用了,也就是客户端不在拥有原型对象 取而代之的是通过管理器获取 Client 客户端角色 向管理员发起创建对象的请求 Prototype、ConcretePrototype 角色与之前的概念相同 PrototypeManager 角色 原型管理器角色,负责原型对象的创建和管理示例代码
在原来的基础上增加原型管理器package prototype; import java.util.HashMap; public class PrototypeManager { /*hashMap维护原型对象 * */ private HashMap测试类Client角色中也增加相关代码 看得出来,从对象管理器中获取的对象,都是原有对象的一个clone 并不是相同的对象 带管理器的原型模式也叫做 登记形式的原型模式 登记就是相当于在管理器中备案map = new HashMap<>(); /*饿汉式单例模式返回创建原型对象管理器 逻辑上原型对象管理器只有一个 * */ private static PrototypeManager prototypeManager= new PrototypeManager(); public static PrototypeManager getPm(){ return prototypeManager; } /*初始化内置两个原型对象 * */ private PrototypeManager(){ map.put("product1",new ConcreatePrototype()); map.put("product2",new ConcreatePrototype()); } /*提供了添加原型对象方法*/ public void add(String key,Prototype prototype){ map.put(key,prototype); } /*提供了获取对象的方法,获取的对象依赖的是clone,而不是保存进去的对象*/ public Prototype get(String key){ return ((Prototype)map.get(key)).clone(); } }