原型模式属于5种创建型的一种,顾名思义,它是以某个对象为原型构造出与原型对象一模一样的“复制品”。这种对象创建模式可类比孙悟空拔一根毫毛吹出十万个与自己一样的美猴王。很多编程语言都提供了一种clone机制,可以快捷地实现出原型模式。
一、意图
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
二、适用场景
“当一个系统应该独立于它的产品创建、构成和表示时,要使用原型模式”,以及当一个类的实例只能有几个不同状态组合中的一种时,建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便。”(GOF书上的原话,读完有种“听君一席话,如听一席话”的感觉)。
下面是摘自“菜鸟教程”的一段话:
- 资源优化场景。
- 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
- 性能和安全要求的场景。
- 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
- 一个对象多个修改者的场景。
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
- 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。
三、参与者
- Prototype,一个声明克隆自身的接口,Java中是Cloneable;
- AbstractPrototype,Prototype的抽象类
- ConcretePrototype,一个实现克隆自身的操作;
- PrototypeManager,原型管理器;
- Client,客户类
四、UML类图
五、代码实现
1. 抽象类
public abstract class DBConnection implements Cloneable {
private Integer connId;
protected String type;
abstract void connected();
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
2. 具体类
class MysqlConn extends DBConnection {
public MysqlConn() {
type = "Mysql";
}
void connected() {
System.out.println("Mysql has connected");
}
}
class OracleConn extends DBConnection {
public OracleConn() {
type = "Oracle";
}
void connected() {
System.out.println("Oracle has connected");
}
}
class AccessConn extends DBConnection {
public AccessConn() {
type = "Access";
}
void connected() {
System.out.println("MS Access has connected");
}
}
3. 管理类
public class ConnectionManager {
private static Map<String, DBConnection> connectionMap = new HashMap<String, DBConnection>();
public static DBConnection getConnectionByType(String type) throws CloneNotSupportedException {
DBConnection dbConnection = connectionMap.get(type);
return (DBConnection) dbConnection.clone();
}
public static void createCache() {
DBConnection mysql = new MysqlConn();
connectionMap.put(mysql.type, mysql);
DBConnection access = new AccessConn();
connectionMap.put(access.type, access);
DBConnection oracle = new OracleConn();
connectionMap.put(oracle.type, oracle);
}
}
4. 客户类
public class PrototypeTest {
@Test
public void testProtoType() throws CloneNotSupportedException {
ConnectionManager.createCache();
DBConnection mysql = ConnectionManager.getConnectionByType("Mysql");
mysql.connected();
DBConnection oracle = ConnectionManager.getConnectionByType("Oracle");
oracle.connected();
DBConnection access = ConnectionManager.getConnectionByType("Access");
access.connected();
}
}
5. 输出结果
"D:\Program Files (x86)\android studio\jre\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\lib\idea_rt.jar=1748:D:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\plugins\junit\lib\junit5-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2020.1.3\plugins\junit\lib\junit-rt.jar;D:\Program Files (x86)\android studio\jre\jre\lib\charsets.jar;D:\Program Files (x86)\android studio\jre\jre\lib\ext\access-bridge-64.jar;D:\Program Files (x86)\android studio\jre\jre\lib\ext\cldrdata.jar;D:\Program Files (x86)\android studio\jre\jre\lib\ext\dnsns.jar;D:\Program Files (x86)\android studio\jre\jre\lib\ext\jaccess.jar;D:\Program Files (x86)\android studio\jre\jre\lib\ext\localedata.jar;D:\Program Files (x86)\android studio\jre\jre\lib\ext\nashorn.jar;D:\Program Files (x86)\android studio\jre\jre\lib\ext\sunec.jar;D:\Program Files (x86)\android studio\jre\jre\lib\ext\sunjce_provider.jar;D:\Program Files (x86)\android studio\jre\jre\lib\ext\sunmscapi.jar;D:\Program Files (x86)\android studio\jre\jre\lib\ext\sunpkcs11.jar;D:\Program Files (x86)\android studio\jre\jre\lib\ext\zipfs.jar;D:\Program Files (x86)\android studio\jre\jre\lib\jce.jar;D:\Program Files (x86)\android studio\jre\jre\lib\jsse.jar;D:\Program Files (x86)\android studio\jre\jre\lib\management-agent.jar;D:\Program Files (x86)\android studio\jre\jre\lib\resources.jar;D:\Program Files (x86)\android studio\jre\jre\lib\rt.jar;D:\MyDocs\SELF\MOP\Projects\design-patterns\target\test-classes;D:\Users\Administrator\m2\newRepository\junit\junit\4.12\junit-4.12.jar;D:\Users\Administrator\m2\newRepository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;D:\Users\Administrator\m2\newRepository\org\springframework\boot\spring-boot-starter\2.7.0\spring-boot-starter-2.7.0.jar;D:\Users\Administrator\m2\newRepository\org\springframework\boot\spring-boot\2.7.0\spring-boot-2.7.0.jar;D:\Users\Administrator\m2\newRepository\org\springframework\spring-context\5.3.20\spring-context-5.3.20.jar;D:\Users\Administrator\m2\newRepository\org\springframework\spring-aop\5.3.20\spring-aop-5.3.20.jar;D:\Users\Administrator\m2\newRepository\org\springframework\spring-beans\5.3.20\spring-beans-5.3.20.jar;D:\Users\Administrator\m2\newRepository\org\springframework\spring-expression\5.3.20\spring-expression-5.3.20.jar;D:\Users\Administrator\m2\newRepository\org\springframework\boot\spring-boot-autoconfigure\2.7.0\spring-boot-autoconfigure-2.7.0.jar;D:\Users\Administrator\m2\newRepository\org\springframework\boot\spring-boot-starter-logging\2.7.0\spring-boot-starter-logging-2.7.0.jar;D:\Users\Administrator\m2\newRepository\ch\qos\logback\logback-classic\1.2.11\logback-classic-1.2.11.jar;D:\Users\Administrator\m2\newRepository\ch\qos\logback\logback-core\1.2.11\logback-core-1.2.11.jar;D:\Users\Administrator\m2\newRepository\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;D:\Users\Administrator\m2\newRepository\org\apache\logging\log4j\log4j-to-slf4j\2.17.2\log4j-to-slf4j-2.17.2.jar;D:\Users\Administrator\m2\newRepository\org\apache\logging\log4j\log4j-api\2.17.2\log4j-api-2.17.2.jar;D:\Users\Administrator\m2\newRepository\org\slf4j\jul-to-slf4j\1.7.36\jul-to-slf4j-1.7.36.jar;D:\Users\Administrator\m2\newRepository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\Users\Administrator\m2\newRepository\org\springframework\spring-core\5.3.20\spring-core-5.3.20.jar;D:\Users\Administrator\m2\newRepository\org\springframework\spring-jcl\5.3.20\spring-jcl-5.3.20.jar;D:\Users\Administrator\m2\newRepository\org\yaml\snakeyaml\1.30\snakeyaml-1.30.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 prototype.PrototypeTest,testProtoType
Mysql has connected
Oracle has connected
MS Access has connected
Process finished with exit code 0
六、补充
对象拷贝时,要根据实际需要选择浅拷贝和深拷贝,Object#clone()的默认实现是浅拷贝;而实现深拷贝有两种方法:一是手动实现嵌套对象的拷贝,二是用序列化和反序列化的方法来实现对象的拷贝。
七、参考
https://morioh.com/p/fa66c4f6eb23
https://springframework.guru/gang-of-four-design-patterns/prototype-pattern/
https://dzone.com/articles/using-prototype-design-pattern-in-java(推荐)
https://www.javadevjournal.com/java-design-patterns/prototype-design-pattern/
https://refactoringguru.cn/design-patterns/prototype
https://www.geeksforgeeks.org/prototype-design-pattern/
https://www.runoob.com/design-pattern/prototype-pattern.html
设计模式系列博文导航
一、创建型 - 5种
原型模式(Prototype Pattern)
抽象工厂模式(Abstract Factory Pattern)
建造者模式(Builder Pattern)
工厂模式(Factory Pattern)
单例模式(Singleton Pattern)
助记语:原抽建工单
二、结构型 - 8种
享元模式(Flyweight Pattern)
代理模式(Proxy Pattern)
适配器模式(Adapter Pattern)
外观模式(Facade Pattern)
过滤器模式(Filter/Criteria Pattern)
桥接模式(Bridge Pattern)
组合模式(Composite Pattern)
装饰器模式(Decorator Pattern)
助记语:想呆室外,过桥组装
三、行为型 - 11种
责任链模式(Chain of Responsibility Pattern)
命令模式(Command Pattern)
解释器模式(Interpreter Pattern)
中介者模式(Mediator Pattern)
迭代器模式(Iterator Pattern)
观察者模式(Observer Pattern)
策略模式(Strategy Pattern)
状态模式(State Pattern)
备忘录模式(Memento Pattern)
模板方法模式(Template Pattern)
访问者模式(Visitor Pattern)
助记语:责令解中谍,观测状被模仿