原型模式

序言:今天我们来聊一下原型模式,我个人认为原型模式的命名不太好理解,称呼其为克隆模式会更妥当一点。原型模式的目的是通过复制一个现有的对象来生成一个新的对象,而不是通过实例化的方法。

原型模式的基本介绍

  • 用已经创建的实例对象作为原型,通过复制对象来创建一个和原型相同的对象或相似的新对象

  • 原型模式属于创建型模式,主要通过 Cloneable 接口去完成 对象的复制

在原型模式结构图中,会有这么几个角色

  • 抽象原型角色(Prototype):是声明克隆方法的接口,是所有原型类的公共父类

  • 具体原型角色(Realizetype):它实现在抽象原型类中所声明的克隆方法,在克隆方法中返回一个克隆对象

  • 访问角色(PrototypeTest): 使用具体原型类中的 clone() 方法来复制新的对象

需要注意的点:

在 Java 中 能够克隆的 Java类 务必得 实现 Cloneable 接口,表示这个 类 能够被 “复制”,至于这个 复制的效果 则与我们的实现有关,通常 clone()方法满足以下的条件:

  • 对任何的对象x,都有:x.clone()!=x 。换言之,克隆对象与元对象不是一个对象

  • 对任何的对象x,都有:x.clone().getClass==x.getClass(),换言之,克隆对象与元对象的类型一样

  • 对任何的对象x,如果 equals() 方法编写得当的话, 那么x.clone().equals(x)应该是成立的

在正式开始原型模式之前,我们先了解两个概念 浅克隆和深克隆浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制

原型模式(浅克隆)

在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址 复制 一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向 相同 的内存地址

下面我们以 花园和花 为案例

假设我们有一个花园,我们在花园里种植上好的牡丹花,有一天突然想照着牡丹园再建一个一模一样的园子,只是在花园中改种玫瑰

/**
 * 花园
 */
@Data
@AllArgsConstructor
public class Garden implements Cloneable {

//面积
private double area;
private Flower flower;

@Override
protected Garden clone() {
Garden garden = null;
try {
garden = (Garden) super.clone();
} catch (Exception e) {
e.printStackTrace();
}
return garden;
}

}
@Data
@AllArgsConstructor
public class Flower {

private String name;
private String color;

}

下面来测试一下原型模式(浅克隆)

public class Client {
public static void main(String[] args) {
// 牡丹花
Flower peony = new Flower("牡丹", "blue");
// 牡丹园
Garden peonyGarden = new Garden(1000, peony);
System.out.println("初始的牡丹园:" + peonyGarden);
// 牡丹园的复制建造 但是花改成玫瑰 改名玫瑰园
Garden roseGarden = peonyGarden.clone();
//浅复制只会复制引用地址,并没有重新复制一个对象
System.out.println(peonyGarden.getFlower() == roseGarden.getFlower());
// 改为种植玫瑰花
roseGarden.getFlower().setName("玫瑰");
roseGarden.getFlower().setColor("red");
System.out.println("玫瑰园:" + roseGarden);
System.out.println("牡丹园:" + peonyGarden);
}
}

我们发现,我们想在新开拓的花园中改种玫瑰后,会影响原花园的花卉的品种,这显然不是我们想要的效果

从这里我们也看出来了,浅克隆 在克隆一个对象的 引用类型的成员变量时 只是复制其地址值,并没有复制该对象

原型模式(深克隆)

在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
 
在 Java 中想实现 深克隆,通常有 两种方式

  • 对于克隆对象的 引用类型,逐层克隆

  • 使用序列化方式

逐层克隆

逐层克隆意味着,如果我们要拷贝一个对象,该对象中 若有多个引用类型的成员变量,它们都要实现克隆方法,若嵌套多层引用类型的成员变量,则逐层 实现 Cloneable 接口

**
 * 花园
 */
@Data
@AllArgsConstructor
public class Garden implements Cloneable {

//面积
private double area;
private Flower flower;

@Override
protected Garden clone() {
Garden garden = null;
try {
garden = (Garden) super.clone();
garden.flower = garden.flower.clone();
} catch (Exception e) {
e.printStackTrace();
}
return garden;
}

}
@Data
@AllArgsConstructor
public class Flower implements Cloneable {

private String name;
private String color;

@Override
protected Flower clone() {
Flower flower = null;
try {
flower = (Flower) super.clone();
} catch (Exception e) {
e.printStackTrace();
}
return flower;
}

}

下面我们测试一下,使用逐层实现 Cloneable 接口 而完成的深克隆

public class Client {
public static void main(String[] args) {
// 牡丹花
Flower peony = new Flower("牡丹", "blue");
// 牡丹园
Garden peonyGarden = new Garden(1000, peony);
// 牡丹园的复制建造 但是花改成玫瑰 改名玫瑰园
Garden roseGarden = peonyGarden.clone();
// 深克隆 面对引用类型的成员变量 也重新复制了一个对象
System.out.println(peonyGarden.getFlower() == roseGarden.getFlower());
// 改为种植玫瑰花
roseGarden.getFlower().setName("玫瑰");
roseGarden.getFlower().setColor("red");
System.out.println("玫瑰园:" + roseGarden);
System.out.println("牡丹园:" + peonyGarden);
}
}

序列化

深克隆模式,采取序列化这种方式可能更简单一些,所以的引用类型 成员变量,都实现序列化接口,原型对象 自实现 deepClone 方法即可

/**
 * 花园
 */
@Data
@AllArgsConstructor
public class Garden implements Serializable {

private static final long serialVersionUID = 2231850409918603998L;
//面积
private double area;
private Flower flower;

//深拷贝-方式2 使用序列化方式
public Garden deepClone(){
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
Garden garden = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this); //当前这个对象以对象的方式输出

//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
garden = (Garden) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return garden;
}

}
@Data
@AllArgsConstructor
public class Flower implements Serializable {

private static final long serialVersionUID = 126839939664064143L;

private String name;
private String color;

@Override
protected Flower clone() {
Flower flower = null;
try {
flower = (Flower) super.clone();
} catch (Exception e) {
e.printStackTrace();
}
return flower;
}

}

下面我们进行序列化深克隆的测试

public class Client {
public static void main(String[] args) {
// 牡丹花
Flower peony = new Flower("牡丹", "blue");
// 牡丹园
Garden peonyGarden = new Garden(1000, peony);
// 牡丹园的复制建造 但是花改成玫瑰 改名玫瑰园
Garden roseGarden = peonyGarden.deepClone();
// 深克隆 面对引用类型的成员变量 也重新复制了一个对象
System.out.println(peonyGarden.getFlower() == roseGarden.getFlower());
// 改为种植玫瑰花
roseGarden.getFlower().setName("玫瑰");
roseGarden.getFlower().setColor("red");
System.out.println("玫瑰园:" + roseGarden);
System.out.println("牡丹园:" + peonyGarden);
}
}

从上述我们可以看出,深克隆不仅在堆内存上开辟了空间以存储复制出的对象,甚至连对象中的引用类型的属性所指向的对象也得以复制,重新开辟了堆空间存储。

(0)

相关推荐

  • Prototype原型模式

    >>返回<C#常用设计模式> 1. 简介 2. 示例 3. 优缺点 3.1. 优点 3.2. 缺点 3.3. 原型模式的适用环境 1. 简介 定义 使用原型实例指定待创建对象的 ...

  • Photoshop-Ps-风格化干草文字-第1节

    教程未使用Adobe Photoshop最新版本,但其技术和过程仍然很重要. 1.基本图层和布局 第1步 打开素材图片,打开菜单,图像>图像大小,更改参数. 第2步 将原始图片作为智能对象导入. ...

  • 设计模式-原型模式详解

    一.原型模式的概念 原型模式属于创建型设计模式.当要创建的对象类型由原型实例确定时使用它,该实例被克隆以生成新对象. 此模式用于 1.避免客户端应用程序中的对象创建者的子类,如工厂方法模式. 2.避免 ...

  • 结合JDK源码看设计模式——原型模式

    定义: 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.不需要知道任何创建的细节,不调用构造函数 适用场景: 类初始化的时候消耗较多资源 new产生的对象需要非常繁琐的过程 构造函数比 ...

  • 面试官:你知道对象的克隆原理吗?

    本文主要内容 背景 先说说生活中三个例子: 西游记中,孙悟空能变身为n多个孙悟空,也就是一个孙悟空克隆为多个孙悟空了. 王者农药中,元歌有个傀儡,这个傀儡我们也可理解为复制的元歌,你把傀儡杀死了,其实 ...

  • 创建型模式之原型模式

    定义与特点 原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象. 在这里,原型实例指定了要创建的对象的种类.用这种方式创建对象 ...

  • 设计模式-原型模式

    原型模式 ​原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,并且通过拷贝这些原型创建新的对象 ​调用者不需要知道任何创建细节,不调用构造函数 ​其属于一种创建型模式 通用类图 优点 性能 ...

  • 大白话原型模式(Prototype Pattern)

    意图 原型模式是创建型设计模式,可以复制已存在的对象而无需依赖它的类. 问题 假如现在有一个对象,我们想完全复制一份新的,我们该如何做? 创建同一个类的新对象 遍历所有已存在对象的值,然后将他们的值复 ...

  • 五分钟 掌握 原型模式 (文末送书)

    回复"000"获取程序员必备电子书 大家好,我是老田,今天我给大家分享设计模式中的原型模式.用贴切的生活故事,以及真实项目场景来讲设计模式,最后用一句话来总结这个设计模式. 关于设 ...

  • [js] 第109天 请描述下什么是原型模式?它主要运用在哪些场景?

    今日试题: 请描述下什么是原型模式?它主要运用在哪些场景? 此开源项目四大宗旨:勤思考,多动手,善总结,能坚持 <论语>,曾子曰:"吾日三省吾身"(我每天多次反省自己) ...

  • PHP设计模式之原型模式

    PHP设计模式之原型模式 原型模式其实更形象的来说应该叫克隆模式.它主要的行为是对对象进行克隆,但是又把被克隆的对象称之为最初的原型,于是,这个模式就这样被命名了.说真的,从使用方式来看真的感觉叫克隆 ...

  • [PHP小课堂]PHP设计模式之原型模式

    [PHP小课堂]PHP设计模式之原型模式 关注公众号:[硬核项目经理]获取最新文章 添加微信/QQ好友:[DarkMatterZyCoder/149844827]免费得PHP.项目管理学习资料

  • PrototypePattern原型模式

    原型模式 1.定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象 Java自带一个Cloneable接口,原型类实现该接口并重写clone方法,通过调用该方法创建新的对象.这种不通过 ...