设计模式——原型模式(创建型)

news2025/6/26 23:38:55

引言

原型模式是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类。

问题

如果你有一个对象, 并希望生成与其完全相同的一个复制品, 你该如何实现呢? 首先, 你必须新建一个属于相同类的对象。 然后, 你必须遍历原始对象的所有成员变量, 并将成员变量值复制到新对象中。

不错! 但有个小问题。 并非所有对象都能通过这种方式进行复制, 因为有些对象可能拥有私有成员变量, 它们在对象本身以外是不可见的。

直接复制还有另外一个问题。 因为你必须知道对象所属的类才能创建复制品, 所以代码必须依赖该类。 即使你可以接受额外的依赖性, 那还有另外一个问题: 有时你只知道对象所实现的接口, 而不知道其所属的具体类, 比如可向方法的某个参数传入实现了某个接口的任何对象。

解决方案

原型模式将克隆过程委派给被克隆的实际对象。 模式为所有支持克隆的对象声明了一个通用接口, 该接口让你能够克隆对象, 同时又无需将代码和对象所属类耦合。 通常情况下, 这样的接口中仅包含一个 克隆方法。

所有的类对 克隆方法的实现都非常相似。 该方法会创建一个当前类的对象, 然后将原始对象所有的成员变量值复制到新建的类中。 你甚至可以复制私有成员变量, 因为绝大部分编程语言都允许对象访问其同类对象的私有成员变量。

支持克隆的对象即为原型。 当你的对象有几十个成员变量和几百种类型时, 对其进行克隆甚至可以代替子类的构造。

 

 其运作方式如下: 创建一系列不同类型的对象并不同的方式对其进行配置。 如果所需对象与预先配置的对象相同, 那么你只需克隆原型即可, 无需新建一个对象。

真实世界类比

现实生活中, 产品在得到大规模生产前会使用原型进行各种测试。 但在这种情况下, 原型只是一种被动的工具, 不参与任何真正的生产活动。

 由于工业原型并不是真正意义上的自我复制, 因此细胞有丝分裂 (还记得生物学知识吗?) 或许是更恰当的类比。 有丝分裂会产生一对完全相同的细胞。 原始细胞就是一个原型, 它在复制体的生成过程中起到了推动作用。

原型模式结构

 

伪代码

在本例中, 原型模式能让你生成完全相同的几何对象副本, 同时无需代码与对象所属类耦合。

 所有形状类都遵循同一个提供克隆方法的接口。 在复制自身成员变量值到结果对象前, 子类可调用其父类的克隆方法。

// 基础原型。
abstract class Shape is
    field X: int
    field Y: int
    field color: string

    // 常规构造函数。
    constructor Shape() is
        // ……

    // 原型构造函数。使用已有对象的数值来初始化一个新对象。
    constructor Shape(source: Shape) is
        this()
        this.X = source.X
        this.Y = source.Y
        this.color = source.color

    // clone(克隆)操作会返回一个形状子类。
    abstract method clone():Shape


// 具体原型。克隆方法会创建一个新对象并将其传递给构造函数。直到构造函数运
// 行完成前,它都拥有指向新克隆对象的引用。因此,任何人都无法访问未完全生
// 成的克隆对象。这可以保持克隆结果的一致。
class Rectangle extends Shape is
    field width: int
    field height: int

    constructor Rectangle(source: Rectangle) is
        // 需要调用父构造函数来复制父类中定义的私有成员变量。
        super(source)
        this.width = source.width
        this.height = source.height

    method clone():Shape is
        return new Rectangle(this)


class Circle extends Shape is
    field radius: int

    constructor Circle(source: Circle) is
        super(source)
        this.radius = source.radius

    method clone():Shape is
        return new Circle(this)


// 客户端代码中的某个位置。
class Application is
    field shapes: array of Shape

    constructor Application() is
        Circle circle = new Circle()
        circle.X = 10
        circle.Y = 10
        circle.radius = 20
        shapes.add(circle)

        Circle anotherCircle = circle.clone()
        shapes.add(anotherCircle)
        // 变量 `anotherCircle(另一个圆)`与 `circle(圆)`对象的内
        // 容完全一样。

        Rectangle rectangle = new Rectangle()
        rectangle.width = 10
        rectangle.height = 20
        shapes.add(rectangle)

    method businessLogic() is
        // 原型是很强大的东西,因为它能在不知晓对象类型的情况下生成一个与
        // 其完全相同的复制品。
        Array shapesCopy = new Array of Shapes.

        // 例如,我们不知晓形状数组中元素的具体类型,只知道它们都是形状。
        // 但在多态机制的帮助下,当我们在某个形状上调用 `clone(克隆)`
        // 方法时,程序会检查其所属的类并调用其中所定义的克隆方法。这样,
        // 我们将获得一个正确的复制品,而不是一组简单的形状对象。
        foreach (s in shapes) do
            shapesCopy.add(s.clone())

        // `shapesCopy(形状副本)`数组中包含 `shape(形状)`数组所有
        // 子元素的复制品。

原型模式适合应用场景

 如果你需要复制一些对象, 同时又希望代码独立于这些对象所属的具体类, 可以使用原型模式。

 这一点考量通常出现在代码需要处理第三方代码通过接口传递过来的对象时。 即使不考虑代码耦合的情况, 你的代码也不能依赖这些对象所属的具体类, 因为你不知道它们的具体信息。

原型模式为客户端代码提供一个通用接口, 客户端代码可通过这一接口与所有实现了克隆的对象进行交互, 它也使得客户端代码与其所克隆的对象具体类独立开来。

 如果子类的区别仅在于其对象的初始化方式, 那么你可以使用该模式来减少子类的数量。 别人创建这些子类的目的可能是为了创建特定类型的对象。

 在原型模式中, 你可以使用一系列预生成的、 各种类型的对象作为原型。

客户端不必根据需求对子类进行实例化, 只需找到合适的原型并对其进行克隆即可。

 实现方式

  1. 创建原型接口, 并在其中声明 克隆方法。 如果你已有类层次结构, 则只需在其所有类中添加该方法即可。

  2. 原型类必须另行定义一个以该类对象为参数的构造函数。 构造函数必须复制参数对象中的所有成员变量值到新建实体中。 如果你需要修改子类, 则必须调用父类构造函数, 让父类复制其私有成员变量值。

    如果编程语言不支持方法重载, 那么你可能需要定义一个特殊方法来复制对象数据。 在构造函数中进行此类处理比较方便, 因为它在调用 new运算符后会马上返回结果对象。

  3. 克隆方法通常只有一行代码: 使用 new运算符调用原型版本的构造函数。 注意, 每个类都必须显式重写克隆方法并使用自身类名调用 new运算符。 否则, 克隆方法可能会生成父类的对象。

  4. 你还可以创建一个中心化原型注册表, 用于存储常用原型。

    你可以新建一个工厂类来实现注册表, 或者在原型基类中添加一个获取原型的静态方法。 该方法必须能够根据客户端代码设定的条件进行搜索。 搜索条件可以是简单的字符串, 或者是一组复杂的搜索参数。 找到合适的原型后, 注册表应对原型进行克隆, 并将复制生成的对象返回给客户端。

    最后还要将对子类构造函数的直接调用替换为对原型注册表工厂方法的调用。

原型模式优缺点

  •  你可以克隆对象, 而无需与它们所属的具体类相耦合。
  •  你可以克隆预生成原型, 避免反复运行初始化代码。
  •  你可以更方便地生成复杂对象。
  •  你可以用继承以外的方式来处理复杂对象的不同配置。

与其他模式的关系

  • 在许多设计工作的初期都会使用工厂方法模式 (较为简单,而且可以更方便地通过子类进行定制),随后演化为使用抽象工厂模式、原型模式或生成器模式(更灵活但更加复杂)。
  • 抽象工厂模式通常基于一组工厂方法,但你也可以使用原型模式来生成这些类的方法。
  • 原型可用于保存命令模式的历史记录。
  • 大量使用组合模式和装饰模式的设计通常可从对于原型的使用中获益。你可以通过该模式来复制复杂结构,而非从零开始重新构造。
  • 原型并不基于继承,因此没有继承的缺点。另一方面,原型需要对被复制对象进行复杂的初始化。工厂方法基于继承,但是它不需要初始化步骤。
  • 有时候原型可以作为备忘录模式的一个简化版本,其条件是你需要在历史记录中存储的对象的状态比较简单,不需要链接其他外部资源,或者链接可以方便地重建。
  • 抽象工厂、生成器和原型都可以用单例模式来实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1306114.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【vue实战项目】通用管理系统:信息列表,信息的编辑和删除

本文为博主的vue实战小项目系列中的第七篇,很适合后端或者才入门的小伙伴看,一个前端项目从0到1的保姆级教学。前面的内容: 【vue实战项目】通用管理系统:登录页-CSDN博客 【vue实战项目】通用管理系统:封装token操作…

【日积月累】Spring中的AOP与IOC相关问题详解

Spring中的AOP与IOC 1.前言2.Spring AOP(面向切面编程)2.1 AOP的实现过程2.2 AOP代理模式的类型2.2.1JDK的动态代理2.2.2CGLIB的动态代理 2.3AOP应用常见场景2.3.1日志记录 2.4对AOP的理解 3.Spring IOC(Inversion of Control,控…

【JVM从入门到实战】(五)类加载器

一、什么是类加载器 类加载器(ClassLoader)是Java虚拟机提供给应用程序去实现获取类和接口字节码数据的技术。 类加载器只参与加载过程中的字节码获取并加载到内存这一部分。 二、jdk8及之前的版本 类加载器分为三类: 启动类加载器-加载Ja…

Linux驱动入门 —— LED点灯驱动程序

目录 IMX6ULL 的 GPIO 操作方法 GPIO 操作相关名词 IMX6ULL 的 GPIO 模块结构 GPIO 模块内部 读 GPIO​编辑 写 GPIO​编辑 LED 点灯驱动程序 字符设备驱动程序框架 编写驱动程序的步骤: 先编写驱动程序代码: 再编写测试程序代码:…

【华为数据之道学习笔记】4-2信息架构原则:建立企业层面的共同行为准则

信息架构承载了企业如何管理数据资产的方法,需要从整个企业 层面制订统一的原则,这些原则不仅是对数据专业人员的要求,也是对业务的要求,因为业务才是真正的数据Owner。所以,公司所有业务部门都应该共同遵从信息架构原…

【人工智能】人工智能中的Agent:法律虚拟助手简单示例

人工智能中的Agent:法律虚拟助手简单示例 随着人工智能技术的发展,Agent(代理)的概念在这个领域中变得愈发重要。在人工智能的应用中,Agent可以是一个系统、软件或机器人,能够执行特定的任务,理…

【JNA与C++基本使用示例】

JNA中java与C使用注意事项和代码示例 JNA关系映射表使用案列注意代码示例C代码java代码 JNA关系映射表 使用案列 注意 JNA只支持C方式的dll使用C的char* 作为返回值时,需要返回的变量为malloc分配的地址C的strlen函数只获得除/0以外的字符串长度 代码示例 C代码…

jumpserver web终端无法展示资产信息

前言 最近搭建了jumpServer 然后在web终端无法展示资产信息 错误展示 我的资产列表里面是有资产的 解决办法:

element Transfer 穿梭框 内容太长显示不全,鼠标移动上去显示全部

element Transfer中文字太长会造成显示不全&#xff0c;然后加了个提示 我这边是加了个插槽的形式&#xff0c;根据长度判断的&#xff0c;这个有个弊端就是如果是手机号的话&#xff0c;可能会没有省略号&#xff0c;然后也会有个黑色的提示框 <el-transferfilterable :d…

基于SSM实现的精品课程网站

一、系统架构 前端&#xff1a;jsp | js | css | jquery | bootstrap 后端&#xff1a;spring | springmvc | mybatis 环境&#xff1a;jdk1.7 | mysql | maven | tomcat 二、代码及数据库 三、功能介绍 01. 登录页 02. web端-首页 03. web端-视频教程 04. web端-资料…

【React】在移动端使用iconfont图标库

1、首先将需要的图标加入购物车 2、点击右上角的购物车&#xff0c;将购物里的图标添加到项目中 3、点击添加项目就会有选择的项目&#xff0c;选择后保存 如果有项目可以直接选择需要添加的项目&#xff0c;没有的需要新建项目存放到项目中 4、确定之后进入我的项目中&…

【初阶C++】前言

C前言 1. 什么是C2. C发展史3. C的重要性4. 如何学习C 1. 什么是C C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对于复杂的问题&#xff0c;规模较大的程序&#xff0c;需要高度的抽象和建模时&#xff0c;C语言则不合适。为了解决软件危机&#xff0c; …

网神 SecGate3600 authManageSet.cgi信息泄露漏洞复现

漏洞概述 网神SecGate 3600 authManageSet.cgi 接口存在敏感信息泄露漏洞&#xff0c;未授权得攻击者可以通过此漏洞获取控制台管理员用户名密码等凭据&#xff0c;可登录控制整个后台&#xff0c;使系统处于极不安全的状态 复现环境 FOFA&#xff1a;body"sec_gate_im…

开源 CAD 计算机辅助设计软件,基于 Node.js 开发,使用浏览器进行访问-供大家学习研究参考

下载&#xff1a;开源CAD计算机辅助设计软件&#xff0c;基于Node.js开发&#xff0c;使用浏览器进行访问-供大家学习研究参考资源-CSDN文库 https://download.csdn.net/download/weixin_43097956/88623022

昇腾910安装驱动出错,降低Centos7.6的内核版本

零、问题描述&#xff1a; 在安装Atlas800-9000服务器的驱动的时候&#xff0c;可能会出现错误&#xff1a;Dkms install failed, details in : /var/log/ascend_seclog/ascend_install.log 如下所示&#xff1a; [rootlocalhost ~]# ./Ascend-hdk-910-npu-driver_23.0.rc3_l…

Python和Beautiful Soup爬虫助力提取文本内容

大家好&#xff0c;网络爬虫是一项非常抢手的技能&#xff0c;收集、分析和清洗数据是数据科学项目中最重要的部分。今天介绍如何从链接中爬取高质量文本内容&#xff0c;我们使用迭代&#xff0c;从大约700个链接中进行网络爬取。如果想直接跳转到代码部分&#xff0c;可以在下…

如何通过IDM进行统一权限

IDM身份管理平台在当今企业信息化建设中扮演着至关重要的角色&#xff0c;它不仅仅关乎用户身份的管理&#xff0c;更牵涉到对企业内部各种资源的合理分配和访问控制。其中&#xff0c;统一权限是 IDM 的一个核心概念&#xff0c;它通过集中管理用户的身份和访问权限&#xff0…

【EventBus】EventBus源码浅析

二、EventBus源码解析 目录 1、EventBus的构造方法2、订阅者注册 2.1 订阅者方法的查找过程2.2 订阅者的注册过程1. subscriptionsByEventType 映射&#xff1a;2. typesBySubscriber 映射&#xff1a;2.3 总结订阅者的注册过程 3、事件的发送 3.1 使用Post提交事件3.2 使用p…

Baumer工业相机堡盟工业相机如何通过BGAPISDK获取相机的各种信息如SN/ID等等(C#)

Baumer工业相机堡盟工业相机如何通过BGAPISDK获取相机的各种信息如SN/ID等等&#xff08;C#&#xff09; Baumer工业相机Baumer工业相机通过SDK获取相关生产信息的技术背景通过SDK获取相机信息的代码分析获取Baumer工业相机相关信息Baumer工业相机相关参数信息获取的测试 Baume…

GoLang EASY 微服务游戏框架 01

1 Overview EASY 是一个go语言编写的框架&#xff0c;兼容性支持go版本1.19&#xff0c;go mod 方式构建管理。它是一个轻型&#xff0c;灵活&#xff0c;自定义适配强的微服务框架。 它支持多种网络协议TCP&#xff0c;websocket&#xff0c;UDP&#xff08;待完成&#xf…