重温23种设计模式(11):原型模式

发布时间:2025-05-18 01:47:33 作者:益华网络 来源:undefined 浏览量(1) 点赞(1)
摘要:来源:mikechen的互联网架构 为什么要学设计模式?设计模式有哪些优点?提升查看框架源码能力提升对复杂业务的代码设计能力以及 code 能力为今后的面试以及进阶之路夯实基础 今天我们要讲的是设计模式中的原型模式(Prototype Pattern)。

来源:mikechen的互联网架构

为什么要学设计模式?设计模式有哪些优点?

提升查看框架源码能力提升对复杂业务的代码设计能力以及 code 能力为今后的面试以及进阶之路夯实基础

今天我们要讲的是设计模式中的原型模式(Prototype Pattern)

原型模式提供了创建对象的最佳方式,主要解决对象复制的问题,适用于创建复杂对象图、实现对象的快照和恢复等场景中

01原型模式的定义

原型模式通过克隆现有对象来创建新对象,避免了频繁的对象实例化过程,属于创建型模式。

简单理解,就是定义一个原型对象作为创建其他对象的基础,通过克隆原型对象,我们可以创建多个具有相同属性和行为的新对象。

例如:

孙悟空有个十分牛逼的绝活儿,叫分身术,可以幻化出多个相同的孙悟空。

这个幻化出的新的分身,和设计模式中的原型模式是相似的。

02原型模式的 UML 类图

原型模式中的 3 个重要角色:

客户端(Client):使用具体原型类中的 clone() 方法,来复制新的对象。

抽象原型(Prototype):可以是抽象类或接口,规定了具体原型对象必须实现的 clone() 方法。

具体原型类(ConcretePrototype):实现抽象原型类的 clone() 方法,它是可被复制的对象。

Prototype 通常不用自己定义,因为拷贝这个操作十分常用。

在 Java 中,提供了Cloneable 接口来支持拷贝操作,它就是原型模式中的 Prototype 。

当然了,原型模式也未必非得去实现 Cloneable 接口,也有其他的实现方式。

03原型模式的两种实现

原型模式有两种拷贝方式:浅拷贝深拷贝

1)浅拷贝

创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。

简单理解,就是只复制所考虑的对象,而不复制它所引用的对象

Object 类提供的方法 clone ,只是拷贝本对象 , 其对象内部的数组、引用对象等都不拷贝。

2)深拷贝

创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

简单理解,就是复制一切,把要复制的对象所引用的对象都拷贝了一遍

相比于浅拷贝,深拷贝速度慢并且开销大,但是拷贝前后两个对象互不影响。

04原型模式的实现示例

源码示例:

将名片拷贝到自己的名片库中。

我们先实现名片类。

具体的原型类:public class BusinessCard implements Cloneable {    private String name;    private String company;    public BusinessCard(){        System.out.println("执行构造函数BusinessCard");    }    public void setName(String name) {        this.name = name;    }    public void setCompany(String company) {        this.company = company;    }    @Override    public BusinessCard clone() {        BusinessCard businessCard = null;        try {            businessCard = (BusinessCard) super.clone();        } catch (CloneNotSupportedException e) {            e.printStackTrace();        }        return businessCard;    }    public void show() {        System.out.println("name:" + name);        System.out.println("company:" + company);    }}

BusinessCard 类实现了 Cloneable 接口,它是一个标识接口,表示这个对象是可拷贝的。

只要重写 clone 方法,就可以实现拷贝。如果实现了 Cloneable 接口、却没有重写 clone 方法,就会报错。

需要注意的是:

clone 方法不是在 Cloneable 接口中定义的(Cloneable 接口中没有定义任何方法),而是在 Object 中定义的。

客户端调用:public class Client {    public static void main(String[] args) {        BusinessCard businessCard = new BusinessCard();        businessCard.setName("钱三");        businessCard.setCompany("阿里");        //拷贝名片        BusinessCard cloneCard1 = businessCard.clone();        cloneCard1.setName("赵四");        cloneCard1.setCompany("百度");        BusinessCard cloneCard2 = businessCard.clone();        cloneCard2.setName("孙五");        cloneCard2.setCompany("腾讯");        businessCard.show();        cloneCard1.show();        cloneCard2.show();    }}

除了第一个名片,其他两个名片都是通过 clone 方法得到的。

但是,clone 方法并不会执行 cloneCard1 和 cloneCard2 的构造函数。

运行结果:

执行构造函数 BusinessCardname:钱三company:阿里name:赵四company:百度name:孙五company:腾讯1)实现浅拷贝上述的例子中,BusinessCard 的字段都是 String 类型的,如果字段是引用的类型的,会出现什么情况呢?public class DeepBusinessCard implements Cloneable {    private String name;    private Company company = new Company();    public void setName(String name) {        this.name = name;    }    public void setCompany(String name, String address) {        this.company.setName(name);        this.company.setAddress(address);    }    @Override    public DeepBusinessCard clone() {        DeepBusinessCard businessCard = null;        try {            businessCard = (DeepBusinessCard) super.clone();        } catch (CloneNotSupportedException e) {            e.printStackTrace();        }        return businessCard;    }    public void show() {        System.out.println("name:" + name);        System.out.println("company:" + company.getName() + "-address-" + company.getAddress());    }}

我们定义了 DeepBusinessCard 类,它的字段 company 是引用类型的。

Company 类:

public class Company {    private String name;    private String address;    public String getAddress() {        return address;    }    public void setAddress(String address) {        this.address = address;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }

在客户端使用 DeepBusinessCard :

public class Client {    public static void main(String[] args) {       DeepBusinessCard businessCard=new DeepBusinessCard();        businessCard.setName("钱三");        businessCard.setCompany("阿里","北京望京");        DeepBusinessCard cloneCard1=businessCard.clone();        cloneCard1.setName("赵四");        cloneCard1.setCompany("百度","北京西二旗");        DeepBusinessCard cloneCard2=businessCard.clone();        cloneCard2.setName("孙五");        cloneCard2.setCompany("腾讯","北京中关村");        businessCard.show();        cloneCard1.show();        cloneCard2.show();    }}

运行结果:

name:钱三company:腾讯-address-北京中关村name:赵四company:腾讯-address-北京中关村name:孙五company:腾讯-address-北京中关村

Object 类提供的 clone 方法,不会拷贝对象中的内部数组和引用对象,导致它们仍旧指向原来对象的内部元素地址。

因此,company 字段为最后设置的”腾讯”、”北京中关村”。

这种拷贝方式,我们就称为浅拷贝

company 字段是引用类型,businessCard 被拷贝后,company 字段仍旧指向原来的 businessCard 对象的 company 字段的地址。

我们每次设置 company 字段,都会覆盖上一次设置的值,最终留下的就是最后一次设置的值:腾讯、北京中关村。

浅拷贝引用关系:

有多个对象可以修改 company ,显然,这样的引用关系是不符合需求的。

为了解决这个问题,我们引入了深拷贝模式

修改引用关系为深拷贝:

拷贝 businessCard 对象的同时,也将它内部的引用对象 company 进行拷贝,使得每个拷贝的对象之间无任何关联,都指向了自身对应的 company。

这种拷贝方式,我们就称为深拷贝

深拷贝模式把要复制的对象所引用的对象都拷贝了一遍。

2)实现深拷贝首先需要修改 Company 类:public class Company implements Cloneable{    private String name;    private String address;    ...    public Company clone(){        Company company=null;        try {            company= (Company) super.clone();        } catch (CloneNotSupportedException e) {            e.printStackTrace();        }        return company;    }}

为了实现 Company 类能被拷贝,Company 类也需要实现 Cloneable 接口、并且覆写 clone 方法。

接着,修改 DeepBusinessCard 的 clone 方法:

public class DeepBusinessCard implements Cloneable {    private String name;    private Company company = new Company();    ...    @Override    public DeepBusinessCard clone() {        DeepBusinessCard businessCard = null;        try {            businessCard = (DeepBusinessCard) super.clone();            businessCard.company = this.company.clone();//1        } catch (CloneNotSupportedException e) {            e.printStackTrace();        }        return businessCard;    }  ...}

注释 1 增加了对 company 字段的拷贝处理,最后在客户端调用。

输出结果:

name:钱三company:阿里-address-北京望京name:赵四company:百度-address-北京西二旗name:孙五company:腾讯-address-北京中关村

可见,采用深拷贝模式修改一个对象的引用类型的成员时,不会再影响另外对象的该成员。

05原型模式的优缺点

优点:

克隆对象时,对客户端隐藏实现细节,避免了代码耦合。

逃避构造函数的约束。

可以动态增加或减少产品类。

原型模式提供了简化的创建结构。

缺点:

必须实现 Cloneable 接口;

clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则

在实现深拷贝时,需要编写较为复杂的代码。

06原型模式的使用场景

不管是复杂还是简单的对象,只要存在对象复制的场景,都适合使用原型模式。

类的初始化需要耗费较多的资源时。

通过 new 产生一个对象、需要非常繁琐的数据准备或访问权限时。

一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时。

07原型模式与工厂方法模式的区别

工厂方法模式是一种创建型设计模式,它提供了一种在不指定具体类的情况下创建对象的方法。

关于工厂方法模式,看这篇:【重温23种设计模式】之工厂方法模式

原型模式与工厂方法模式的主要区别是:对象创建的方式

原型模式通过复制现有的对象来创建新对象,适用于以下场景:

需要创建大量具有相似属性的对象。

对象的创建成本很高,例如需要执行复杂的初始化逻辑。

需要在运行时动态地创建和修改对象。

工厂方法模式通过调用工厂方法来创建新对象,适用于以下场景:

需要创建具有不同类型或不同实现的对象。

对象创建逻辑较为复杂,不适合在客户端代码中直接实例化。

需要将对象创建过程与客户端代码解耦。

08原型模式与单例模式的区别

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。

关于单例模式,看这篇:8大单例模式实现方式总结

原型模式与单例模式的主要区别在于它们的目的

原型模式用于通过复制现有对象来创建新对象,适用于以下场景:

需要创建大量具有相似属性的对象。

对象的创建成本很高,例如需要执行复杂的初始化逻辑。

需要在运行时动态地创建和修改对象。

单例模式用于确保一个类只有一个实例,适用于以下场景:

需要确保一个类只有一个实例。

需要全局访问点以方便地访问该实例。

需要全局共享资源或配置信息。

总结

通过本文,我们了解并掌握了原型模式的概念、原理、应用场景、优缺点、实现方式等。

原型模式是一种强大而灵活的设计模式,它可以简化对象的创建过程、提高性能,具备更好的可维护性。

在实现原型模式时,需要根据具体需求选择使用深拷贝或浅拷贝。原型模式适用于在处理对象创建、动态修改对象结构、高性能缓存等场景中。

原型

二维码

扫一扫,关注我们

声明:本文由【益华网络】编辑上传发布,转载此文章须经作者同意,并请附上出处【益华网络】及本页链接。如内容、图片有任何版权问题,请联系我们进行处理。

感兴趣吗?

欢迎联系我们,我们愿意为您解答任何有关网站疑难问题!

您身边的【网站建设专家】

搜索千万次不如咨询1次

主营项目:网站建设,手机网站,响应式网站,SEO优化,小程序开发,公众号系统,软件开发等

立即咨询 15368564009
在线客服
嘿,我来帮您!