当前位置: 首页 > news >正文

【设计模式】01- 一文理解常用设计模式-“创建型模式”篇

一、前言

最近在复习设计模式,撰写、整理了内容和代码片段,和大家一起交流学习。

设计模式是软件设计中常见问题的典型解决方案。

修改记录

更新内容更新时间
第一版

250212

更新了对文章中的模式代码示范的解释250214

 

二、模式分类

模式可以根据其意图或目的来分类。常见的设计模式包括:

  • 创建型模式提供创建对象的机制, 增加已有代码的灵活性和可复用性;

  • 结构型模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效;

  • 行为型模式负责对象间的高效沟通和职责委派;

本篇文章介绍创建型模式,后续会更新其他两类设计模式。


三、创建型模式概述

创建型模式主要用于对象的创建过程,它隐藏了对象的创建逻辑,使得代码更具灵活性和可维护性。

四、常见创建型模式(配合代码)

1、单例模式(Singleton Pattern)

确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。

// 单例模式示例
const Singleton = (function () {let instance;function createInstance() {const object = new Object({ name: 'Singleton Object' });return object;}return {getInstance: function () {if (!instance) {instance = createInstance();}return instance;}};}());// 使用示例const instance1 = Singleton.getInstance();const instance2 = Singleton.getInstance();console.log(instance1 === instance2); // 输出: true
代码分析
  • 立即执行函数(IIFE)
    • 代码使用了一个立即执行函数 (function () { ... }()),这样可以创建一个独立的作用域,避免变量和函数泄露到全局作用域。
  • instance 变量
    • 在立即执行函数内部声明了一个变量 instance,初始值为 undefined。这个变量用于存储单例对象的实例。
  • createInstance 函数
    • 该函数用于创建单例对象的实例。它使用 new Object() 创建一个新对象,并传入一个包含 name 属性的对象字面量 { name: 'Singleton Object' },最后返回这个新对象。
  • 返回对象
    • 立即执行函数返回一个包含 getInstance 方法的对象。这个对象就是外部可以访问的接口。
  • getInstance 方法
    • 该方法是获取单例对象实例的唯一途径。
    • 首先检查 instance 是否为 undefined(即是否已经创建了实例)。如果 instance 为 undefined,则调用 createInstance 函数创建一个新的实例,并将其赋值给 instance
    • 最后返回 instance。这样,无论调用多少次 getInstance 方法,只要 instance 已经被创建,就会一直返回同一个实例。

代码运行结果

  • 调用 Singleton.getInstance() 方法两次,分别将返回的实例存储在 instance1 和 instance2 中。
  • 使用 === 比较 instance1 和 instance2 是否为同一个对象。由于单例模式确保只有一个实例存在,所以 instance1 和 instance2 是同一个对象,比较结果为 true,并将结果打印到控制台。

2、工厂模式(Factory Pattern)

定义一个创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。

简单理解:工厂模式就像一个工厂,根据不同的需求生产不同的产品。你只需要告诉工厂你需要什么,工厂就会帮你创建出来。

// 工厂模式示例
function CarFactory() {this.createCar = function (type) {let car;if (type === 'sedan') {car = new Sedan();} else if (type === 'suv') {car = new SUV();}car.drive = function () {console.log(`Driving a ${this.constructor.name}`);};return car;};
}
​//createCar 方法:
//该方法接收一个 type 参数,用于指定要创建的汽车类型。
//根据 type 的值,使用 if-else 语句来决定创建哪种类型的汽车对象。如果 type 为 'sedan',则创建一个 Sedan 对象;如果 type 为 'suv',则创建一个 SUV 对象。
//为创建的汽车对象添加一个 drive 方法,该方法会在控制台打印出正在驾驶的汽车类型。这里使用 this.constructor.name 来获取汽车对象的构造函数名,从而得到汽车的类型。
//最后返回创建好的汽车对象。function Sedan() {this.type = 'Sedan';
}
//这是一个简单的构造函数,用于创建轿车对象。在创建 Sedan 对象时,会给对象添加一个 type 属性,值为 'Sedan'。​function SUV() {this.type = 'SUV';
}
​//意思同上// 使用示例
const factory = new CarFactory();
const sedan = factory.createCar('sedan');
const suv = factory.createCar('suv');
​
sedan.drive(); // 输出: Driving a Sedan
suv.drive();   // 输出: Driving a SUV

代码运行结果

  • 创建一个 CarFactory 类的实例 factory
  • 调用 factory.createCar('sedan') 方法创建一个轿车对象,并将其赋值给 sedan
  • 调用 factory.createCar('suv') 方法创建一个 SUV 对象,并将其赋值给 suv
  • 分别调用 sedan.drive() 和 suv.drive() 方法,会在控制台输出相应的驾驶信息,表明成功创建并使用了不同类型的汽车对象。

3、抽象工厂模式(Abstract Factory Pattern)

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

简单理解:就像一个超级工厂,能生产不同品牌的汽车系列产品。

// 抽象工厂模式示例
// 抽象工厂
function AbstractCarFactory() {this.createEngine = function () {throw new Error('This method must be overridden!');};this.createWheel = function () {throw new Error('This method must be overridden!');};
}
//功能:抽象工厂定义了创建发动机和车轮的抽象方法。由于这是抽象工厂,这些方法并没有具体实现,而是抛出一个错误,提醒具体工厂必须重写这些方法。
//作用:为具体工厂提供了一个统一的接口,确保所有具体工厂都实现了创建发动机和车轮的功能。​// 具体工厂:丰田工厂
function ToyotaFactory() {AbstractCarFactory.call(this);this.createEngine = function () {return new ToyotaEngine();};this.createWheel = function () {return new ToyotaWheel();};
}
//继承:使用 AbstractCarFactory.call(this) 继承了抽象工厂的属性和方法。
//方法实现:重写了 createEngine 方法,返回一个 ToyotaEngine 实例;重写了 createWheel 方法,返回一个 ToyotaWheel 实例。
​
// 具体工厂:本田工厂
function HondaFactory() {AbstractCarFactory.call(this);this.createEngine = function () {return new HondaEngine();};this.createWheel = function () {return new HondaWheel();};
}
//​继承:同样继承了抽象工厂的属性和方法。
//方法实现:重写了 createEngine 方法,返回一个 HondaEngine 实例;重写了 createWheel 方法,返回一个 HondaWheel 实例。// 丰田发动机
function ToyotaEngine() {this.start = function () {console.log('Toyota engine started');};
}
​//功能:表示丰田发动机,有一个 start 方法,用于启动发动机并在控制台输出相应信息。// 丰田车轮
function ToyotaWheel() {this.rotate = function () {console.log('Toyota wheel rotating');//功能:表示丰田车轮,有一个 rotate 方法,用于模拟车轮转动并在控制台输出相应信息。};
}
// 本田发动机
function HondaEngine() {this.start = function () {console.log('Honda engine started');};
}
//功能:表示本田发动机,有一个 start 方法,用于启动发动机并在控制台输出相应信息。
​
// 本田车轮
function HondaWheel() {this.rotate = function () {console.log('Honda wheel rotating');};
}
​//功能:表示本田车轮,有一个 rotate 方法,用于模拟车轮转动并在控制台输出相应信息。// 使用示例
const toyotaFactory = new ToyotaFactory();
const toyotaEngine = toyotaFactory.createEngine();
const toyotaWheel = toyotaFactory.createWheel();
​
toyotaEngine.start(); // 输出: Toyota engine started
toyotaWheel.rotate(); // 输出: Toyota wheel rotating
​
const hondaFactory = new HondaFactory();
const hondaEngine = hondaFactory.createEngine();
const hondaWheel = hondaFactory.createWheel();
​
hondaEngine.start(); // 输出: Honda engine started
hondaWheel.rotate(); // 输出: Honda wheel rotating
代码运行结果
  • 创建丰田工厂及部件:创建一个 ToyotaFactory 实例,使用该工厂创建丰田发动机和车轮,并调用它们的方法,输出相应信息。
  • 创建本田工厂及部件:创建一个 HondaFactory 实例,使用该工厂创建本田发动机和车轮,并调用它们的方法,输出相应信息。

4、建造者模式(Builder Pattern)

将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。

简单理解:就像建造房子,有不同的建造步骤,最终可以建成不同风格的房子。

// 建造者模式示例
// 产品类:汽车
function Car() {this.wheels = null;this.engine = null;this.color = null;
​this.showInfo = function () {console.log(`Car with ${this.wheels} wheels, ${this.engine} engine and ${this.color} color`);};
}
//​属性:
//wheels:用于存储汽车的车轮数量,初始值为 null。
//engine:用于存储汽车的发动机类型,初始值为 null。
//color:用于存储汽车的颜色,初始值为 null。
//方法:
//showInfo:该方法会将汽车的车轮数量、发动机类型和颜色信息拼接成字符串,并打印到控制台,方便查看汽车的具体配置。// 建造者类
function CarBuilder() {this.car = new Car();
​this.setWheels = function (wheels) {this.car.wheels = wheels;return this;};
​this.setEngine = function (engine) {this.car.engine = engine;return this;};
​this.setColor = function (color) {this.car.color = color;return this;};
​this.build = function () {return this.car;};
}
//​属性:
//car:在构造函数中创建一个 Car 类的实例,后续的设置操作都是针对这个汽车对象进行的。
方法:
//setWheels:接收一个 wheels 参数,将其赋值给 this.car.wheels,并返回 this(即当前 CarBuilder 实例),这样可以实现链式调用。
//setEngine:接收一个 engine 参数,将其赋值给 this.car.engine,同样返回 this 以支持链式调用。
//setColor:接收一个 color 参数,将其赋值给 this.car.color,也返回 this 用于链式调用。
//build:返回已经构建好的 this.car 对象,标志着汽车构建完成。// 使用示例
const builder = new CarBuilder();
const car = builder.setWheels(4).setEngine('V8').setColor('Red').build();
​
car.showInfo(); // 输出: Car with 4 wheels, V8 engine and Red color

代码运行结果

  • 创建一个 CarBuilder 类的实例 builder
  • 通过链式调用 setWheelssetEngine 和 setColor 方法,依次为汽车设置车轮数量、发动机类型和颜色。
  • 调用 build 方法,得到最终构建好的 Car 对象,并将其赋值给 car
  • 调用 car.showInfo() 方法,在控制台输出汽车的详细信息。

5、原型模式(Prototype Pattern)

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

简单理解:就像复印文件,有一个原始文件作为模板,然后可以复印出很多份一样的文件。

// 原型模式示例
const carPrototype = {brand: 'Unknown',model: 'Unknown',start: function () {console.log(`Starting the ${this.brand} ${this.model}`);},clone: function () {const clone = Object.create(this);return clone;}
};
//属性:
//brand:初始值为 'Unknown',用于表示汽车的品牌。
//model:初始值为 'Unknown',用于表示汽车的型号。
//方法:
//start:该方法会在控制台打印出启动当前汽车(根据 this.brand 和 this.model)的信息。这里的 this 指向调用该方法的对象。
//clone:使用 Object.create(this) 方法创建一个新对象,该新对象继承自当前原型对象(this)。这意味着新对象会拥有原型对象的所有属性和方法,并且对新对象属性的修改不会影响原型对象本身。最后返回这个克隆后的新对象。
​
// 使用示例
const car1 = carPrototype.clone();
car1.brand = 'Toyota';
car1.model = 'Corolla';
​
const car2 = carPrototype.clone();
car2.brand = 'Honda';
car2.model = 'Civic';
​
car1.start(); // 输出: Starting the Toyota Corolla
car2.start(); // 输出: Starting the Honda Civic

代码运行结果

  • 创建 car1 对象
    • 调用 carPrototype.clone() 方法克隆原型对象,得到一个新的汽车对象 car1
    • 分别为 car1 的 brand 和 model 属性赋值为 'Toyota' 和 'Corolla'
  • 创建 car2 对象
    • 同样调用 carPrototype.clone() 方法克隆原型对象,得到另一个新的汽车对象 car2
    • 为 car2 的 brand 和 model 属性赋值为 'Honda' 和 'Civic'
  • 调用 start 方法
    • 分别调用 car1.start() 和 car2.start() 方法,由于 this 指向调用该方法的对象,所以会根据各自对象的 brand 和 model 属性输出相应的启动信息。


五、小结

设计模式在编程当中还是挺重要的,优点包括但不限于:代码复用性更高、可维护性更强、可扩展性更好。

有时间可以花点时间学习/复习一下,相信对我们的编程技术和编程思维会有很多进步~

相关文章:

【设计模式】01- 一文理解常用设计模式-“创建型模式”篇

一、前言 最近在复习设计模式,撰写、整理了内容和代码片段,和大家一起交流学习。 设计模式是软件设计中常见问题的典型解决方案。 修改记录 更新内容更新时间第一版 250212 更新了对文章中的模式代码示范的解释250214 二、模式分类 模式可以根据其意图…...

在ArcGIS JS API中使用WebGL实现波纹扩散特效

在现代WebGIS开发中,ArcGIS JS API 是一个非常强大的工具,它允许开发者创建丰富的地理信息应用。结合WebGL技术,我们可以实现更加复杂和炫酷的可视化效果。本文将介绍如何使用ArcGIS JS API结合WebGL实现一个波纹扩散特效。 波纹扩散效果 1 概…...

我用AI做数据分析之四种堆叠聚合模型的比较

我用AI做数据分析之四种堆叠聚合模型的比较 这里AI数据分析不仅仅是指AI生成代码的能力,我想是测试AI数据分析方面的四个能力,理解人类指令的能力、撰写代码的能力、执行代码的能力和解释结果的能力。如果这四个能力都达到了相当的水准,才可…...

《LSTM与HMM:序列建模领域的双雄对决》

在序列建模的广阔领域中,长短期记忆网络(LSTM)和隐马尔可夫模型(HMM)都是极为重要的工具,它们各自有着独特的优势和应用场景。下面将对两者在序列建模上的异同进行深入探讨。 相同点 序列数据处理能力&…...

Flutter 的 Widget Key 提议大调整?深入聊一聊 Key 的作用

Flutter 的 Widget Key 提议大调整?深入聊一聊 Key 的作用 在 Flutter 里,Key 对象存在的目的主要是区分和维持 Widget 的状态,它是控件在渲染树里的「复用」标识之一,这一点在之前的《深入 Flutter 和 Compose 在 UI 渲染刷新时…...

只需三步!5分钟本地部署deep seek——MAC环境

MAC本地部署deep seek 第一步:下载Ollama第二步:下载deepseek-r1模型第三步:安装谷歌浏览器插件 第一步:下载Ollama 打开此网址:https://ollama.com/,点击下载即可,如果网络比较慢可使用文末百度网盘链接 注:Ollama是…...

网络工程师 (31)VLAN

前言 VLAN(Virtual Local Area Network)即虚拟局域网,是一种将物理局域网划分成多个逻辑上独立的虚拟网络的技术。 一、定义与特点 定义:VLAN是对连接到的第二层交换机端口的网络用户的逻辑分段,不受网络用户的物理位置…...

浏览器网络请求全流程深度解析

一、核心流程概述 现代浏览器的网络请求过程是一个分层协作的精密系统,涉及应用层协议、传输层协议、操作系统内核及网络基础设施的协同工作。整个过程可抽象为以下关键阶段: 请求构建与初始化DNS解析与寻址TCP连接建立HTTP协议交互响应处理与资源解析…...

React历代主要更新

一、React 16之前更新 React Fiber是16版本之后的一种更新机制,使用链表取代了树,是一种fiber数据结构,其有三个指针,分别指向了父节点、子节点、兄弟节点,当中断的时候会记录下当前的节点,然后继续更新&a…...

【数据结构】(8) 二叉树

一、树形结构 1、什么是树形结构 根节点没有前驱,其它节点只有一个前驱(双亲/父结点)。所有节点可以有 0 ~ 多个后继,即分支(孩子结点)。每个结点作为子树的根节点,这些子树互不相交。 2、关于…...

navicat导出表结构到Excel 带字段备注

navicat导出表结构到Excel 带字段备注 SELECTCOLUMN_NAME AS 字段名称,COLUMN_TYPE AS 字段类型, IF( IS_NULLABLE NO, 否, 是 ) AS 是否必填,COLUMN_COMMENT AS 注释 FROMINFORMATION_SCHEMA.COLUMNS WHERE -- 数据库名table_schema vmscenter -- 表名AND table_name y…...

使用pocketpal-ai在手机上搭建本地AI聊天环境

1、下载安装pocketpal-ai 安装github的release APK 2、安装大模型 搜索并下载模型,没找到deepseek官方的,因为海外的开发者上传了一堆乱七八糟的deepseek qwen模型,导致根本找不到官方上传的……deepseek一开源他们觉得自己又行了。 点击之…...

程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<10>

大家好啊,我是小象٩(๑ω๑)۶ 我的博客:Xiao Xiangζั͡ޓއއ 很高兴见到大家,希望能够和大家一起交流学习,共同进步。 今天我们继续来复习指针… 目录 一、看一段代码二、 一维数组传参的本质三、冒泡排序3.1 基本思想四、二…...

FPGA简介|结构、组成和应用

Field Programmable Gate Arrays(FPGA,现场可编程逻辑门阵列),是在PAL、GAL、CPLD等可编程器件的基础上进一步发展的产物, 是作为专用集成电路(ASIC)领域中的一种半定制电路而出现的&#xff0c…...

[c语言日寄]在不完全递增序中查找特定要素

【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋:这是一个专注于C语言刷题的专栏,精选题目,搭配详细题解、拓展算法。从基础语法到复杂算法,题目涉及的知识点全面覆盖,助力你系统提升。无论你是初学者,还是…...

Golang的多团队协作编程模式与实践经验

Golang的多团队协作编程模式与实践经验 一、多团队协作编程模式概述 在软件开发领域,多团队协作编程是一种常见的工作模式。特别是对于大型项目来说,不同团队间需要协同合作,共同完成复杂的任务。Golang作为一种高效、并发性强的编程语言&…...

cv2.Sobel

1. Sobel 算子简介 Sobel 算子是一种 边缘检测算子,通过对图像做梯度计算,可以突出边缘。 Sobel X 方向卷积核: 用于计算 水平方向(x 方向) 的梯度。 2. 输入图像示例 假设我们有一个 55 的灰度图像,像素…...

Windows软件自动化利器:pywinauto python

Pywinauto WindowsAPP UI自动化 Windows软件自动化利器:pywinauto python...

关于 IoT DC3 中驱动(Driver)的理解

在开源IoT DC3物联网系统中,驱动(Driver)扮演着至关重要的角色,它充当了软件系统与物理设备之间的桥梁。驱动的主要功能是依据特定的通信协议连接到设备,并根据设备模板中配置的位号信息进行数据采集和指令控制。不同的…...

LogicFlow自定义节点:矩形、HTML(vue3)

效果: LogicFlow 内部是基于MVVM模式进行开发的,分别使用preact和mobx来处理 view 和 model,所以当我们自定义节点的时候,需要为这个节点定义view和model。 参考官方文档:节点 | LogicFlow 1、自定义矩形节点 custo…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

从面试角度回答Android中ContentProvider启动原理

Android中ContentProvider原理的面试角度解析,分为​​已启动​​和​​未启动​​两种场景: 一、ContentProvider已启动的情况 1. ​​核心流程​​ ​​触发条件​​:当其他组件(如Activity、Service)通过ContentR…...

安卓基础(Java 和 Gradle 版本)

1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...