22.原型模式
1.什么是原型模式?
原型模式是一种创建型模式,是我们能够复制已有对象而又无需使代码依赖他们所属类的一种设计模式。
1.1原型模式解决的问题
首先抛出一个问题:
如果我们有一个类型为Student的对象,并希望生成与其完全相同的克隆对象,我们该如何实现呢?
如果没有原型模式,我们可能的做法是:
首先,新建一个这个类的对象,然后遍历目标对象中的所有成员变量,并将成员变量的值复制到新对象中。(直接复制) 实例代码如下:
public static void main(String[] args) {
Student student = new Student();
student.setName("黄四郎");
student.setNum(1);
student.setAge(40);
student.setWeight(180);
System.out.println(student.toString());
Student student1 = new Student();
System.out.println(student1.toString());
student1.setName(student.getName());
student1.setNum(student.getNum());
student1.setAge(student.getAge());
student1.setWeight(student.getWeight());
System.out.println(student1);
}
这样做在一些情况下可以满足我们的需求,但是有一种情况,我们可能无能为力,那就是我们如何复制目标对象的私有变量。(在不用反射和不提供共有获取方法的情况下) 因为私有变量在对象本身以外是不可见的。
上面我们采用的就是在定义变量的时候提供了公有get方法,所以能顺利复制。
直接复制还有另外一个问题,那就是我们必须知道对象所属的类才能创建复制对象,所以代码必须依赖该类。 即使我们可以接收额外的依赖性,那还有一个问题: 有时我们只知道对象所属的接口,但不知道其所属的具体类。 这时我们又该如何去做?
答案就是使用原型模式
1.2 解决方案: 原型模式
原型模式将对象clone过程委托给被clone的目标对象。也就是说,目标对象的clone过程是在对象内部完成的。
原型模式为所有支持clone的对象声明了一个通用接口( 通常情况下,这样的接口中仅包含一个clone方法),该接口让我们能够clone对象,同时又无需将代码和对象所属类耦合。
UML如下:
所有类对clone的方法实现非常类似,该方法首先会创建一个当前类的对象,然后将原始对象所有成员变量的值复制到新建的类中。(包括私有变量哦) 支持clone的对象我们称之为原型。
下面我们进行实践,将上面的Student类改为原型模式
其中clone的实现是:
@Override
public Student clone() {
Student student = new Student();
student.name = this.name;
student.age = this.age;
student.num = this.num;
student.weight = this.weight;
return student;
}
这样做之后,我们就可以用下面的方式进行克隆:
public static void main(String[] args) {
Student student = new Student();
student.setName("黄四郎");
student.setNum(1);
student.setAge(40);
student.setWeight(180);
System.out.println(student.toString());
Student student1 = student.clone();
System.out.println(student1);
}
当我们的对象有几十个变量和几百种类型时,对其进行clone甚至可以代替子类的构造。
运作方式如下: 创建一系列不同类型对象并用不同的方式进行配置。如果所需对象与预先配置的对象相同,那么我们只需要clone原型即可,无需新建一个对象。
代码地址如下:(其中first包中对应本博客的内容,second是一位大神写的原型模式应用实例)
设计模式/src/main/java/PrototypePattern · 严家豆/Head first 设计模式学习 - 码云 - 开源中国 (gitee.com)
2.原型模式的应用
String的特殊性在于:
因为他为引用型,而且他指向的值为常量,克隆出来的对象改变他的值。实际上是改变了克隆出来对象String类型成员的指向,不会影响被克隆对象的值及其指向。
2.1 Object中的clone
在JDK中提供了接口Cloneable,这时一个标记接口,和Object同包, Object中提供了一个
protected native Object clone() throws CloneNotSupportedException;
它是用来对对象进行克隆的方法。
这就是为什么JDK中如果想要一个类具有克隆能力就必须实现Cloneable接口的原因。注意: 而且如果我们如果不对clone方法进行重写的话,我们使用clone方法得到的对象是浅克隆的。
关于浅克隆和深克隆的差别,请看下面的示例:
class RunoobTest implements Cloneable {
// 声明变量
String name;
int likes;
RunoobTest runoobTest;
public static void main(String[] args) {
// 创建对象
RunoobTest obj1 = new RunoobTest();
RunoobTest inObj = new RunoobTest();
inObj.name = "内部引用对象";
inObj.likes=222;
obj1.runoobTest = inObj;
// 初始化变量
obj1.name = "Runoob";
obj1.likes = 111;
// 打印输出
System.out.println(obj1.name); // Runoob
System.out.println(obj1.likes); // 111
System.out.println(obj1.runoobTest.name);
System.out.println(obj1.runoobTest.likes);
try {
// 创建 obj1 的拷贝
RunoobTest obj2 = (RunoobTest) obj1.clone();
// 使用 obj2 输出变量
System.out.println(obj2.name); // Runoob
System.out.println(obj2.likes); // 111
System.out.println(obj2.runoobTest.name);
System.out.println(obj2.runoobTest.likes);
obj2.name = new String("copy2");//这个操作不会影响obj1的内容,原因是String是final类型的,不可变的。
obj2.runoobTest.name = "copy";//这个操作会印象obj1的内容
} catch (Exception e) {
System.out.println(e);
}
System.out.println(obj1.name); // Runoob
System.out.println(obj1.likes); // 111
System.out.println(obj1.runoobTest.name);
System.out.println(obj1.runoobTest.likes);
}
}
最后本文的内容主要吸收总结与一个很好的设计模式学习网站:
Java 生成器模式讲解和代码示例 (refactoringguru.cn) 推荐大家访问学习
作者:small-water
出处:https://www.cnblogs.com/small-water/p/17870016.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具