【设计模式】02-理解常见设计模式-结构型模式

news2025/2/18 17:32:31

上一篇,我们介绍了设计模式-创建型模式的内容,并给出了相关代码示范。

这一篇我们接着介绍剩下的内容之一“结构型模式”


一、概述

结构型模式主要用于处理类或对象的组合,以获得新的功能或实现更灵活的结构

二、常见的结构型模式

1、适配器模式(Adapter Pattern)

将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。

简单理解:适配器模式就像是一个转接头,当两个接口不兼容时,通过适配器将一个接口转换成另一个接口,使得原本不能一起工作的类可以协同工作。比如你有一个安卓线,但是只有苹果充电头,这时就需要一个适配器来完成适配。

 代码示范

// 旧接口类
class OldInterface {
    specificRequest() {
        return 'Old interface request';
    }
}

// 新接口
class NewInterface {
    constructor() {
        this.oldInterface = new OldInterface();
    }
    request() {
        return this.oldInterface.specificRequest().replace('Old', 'New');
    }
}

// 使用示例
const newObj = new NewInterface();
console.log(newObj.request()); // 输出: New interface request

 代码分析:

class OldInterface {
    specificRequest() {
        return 'Old interface request';
    }
}
  • 这是一个旧的接口类,里面有一个方法 specificRequest
  • 当调用 specificRequest 方法时,它会返回一个字符串 'Old interface request'。这个类就像是一个旧的工具,有它自己特定的功能。

class NewInterface {
    constructor() {
        this.oldInterface = new OldInterface();
    }
    request() {
        return this.oldInterface.specificRequest().replace('Old', 'New');
    }
}

构造函数 constructor

  • 在创建 NewInterface 类的实例时,构造函数会被自动调用。
  • 它创建了一个 OldInterface 类的实例,并把这个实例赋值给 this.oldInterface。也就是说,新接口类持有了旧接口类的一个对象,方便后续使用旧接口的功能。

request 方法:

  • 这个方法是新接口提供的功能。
  • 它首先调用 this.oldInterface.specificRequest(),也就是调用旧接口的 specificRequest 方法,得到字符串 'Old interface request'
  • 然后使用 replace 方法把字符串中的 'Old' 替换成 'New',最终返回 'New interface request'。这个过程就像是把旧工具的输出做了一些修改,让它符合新的要求。

2、装饰器模式(Decorator Pattern)

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

解释:装饰器模式就像给人穿衣服,你可以在不改变人的本质的情况下,通过添加不同的衣服(装饰器)来改变人的外观和功能。在编程中,就是在不改变原有对象结构的前提下,动态地给对象添加额外的职责。

代码示范以及解释

// 基础对象类
class Coffee {
    cost() {
        return 5;
    }
}
//这是一个基础的咖啡类,其中有一个 cost 方法。
//当调用 cost 方法时,它会返回基础咖啡的价格,这里设定为 5 元。可以把它想象成一杯没有任何配料的纯咖啡。

// 装饰器抽象类
class CoffeeDecorator {
    constructor(coffee) {
        this.coffee = coffee;
    }
    cost() {
        return this.coffee.cost();
    }
}
//构造函数 constructor:
//这个构造函数接收一个 coffee 对象作为参数,并将其赋值给 this.coffee。也就是说,装饰器类会持有一个要装饰的咖啡对象。
//cost 方法:
//该方法直接调用被装饰咖啡对象的 cost 方法并返回结果。这个类是所有具体装饰器的基类,它定义了装饰器的基本结构,但本身并没有添加额外的功能。

// 具体装饰器:牛奶
class MilkDecorator extends CoffeeDecorator {
    cost() {
        return this.coffee.cost() + 2;
    }
}
//这个类继承自 CoffeeDecorator 类,它代表给咖啡添加牛奶这个配料。
//cost 方法先调用被装饰咖啡对象的 cost 方法得到原咖啡的价格,然后在此基础上加上 2 元(牛奶的价格),最终返回添加牛奶后咖啡的总成本。

// 具体装饰器:糖
class SugarDecorator extends CoffeeDecorator {
    cost() {
        return this.coffee.cost() + 1;
    }
}
//同样继承自 CoffeeDecorator 类,代表给咖啡添加糖这个配料。
//cost 方法也是先获取原咖啡的价格,再加上 1 元(糖的价格),返回添加糖后咖啡的总成本。

// 使用示例
let coffee = new Coffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);

console.log(coffee.cost()); // 输出: 8

整体运行解释 

 首先创建了一个 Coffee 类的实例 coffee,这是一杯基础的纯咖啡,价格为 5 元。
然后使用 MilkDecorator 对 coffee 进行装饰,此时 coffee 变成了添加了牛奶的咖啡,价格变为 5 + 2 = 7 元。
接着再用 SugarDecorator 对 coffee 进行装饰,此时 coffee 变成了既添加了牛奶又添加了糖的咖啡,价格变为 7 + 1 = 8 元。
最后调用 coffee.cost() 方法并将结果打印到控制台,输出为 8 元。

 3、代理模式(Proxy Pattern)

为其他对象提供一种代理以控制对这个对象的访问。

解释:代理模式就像是一个中介,当你需要访问某个对象时,不直接访问该对象,而是通过代理对象来访问。代理对象可以在访问对象前后添加一些额外的操作,比如权限验证、缓存等。就像你买房,不直接和卖家联系,而是通过房产中介来完成交易。

代码示范以及解释 

// 真实对象类
class RealImage {
    constructor(filename) {
        this.filename = filename;
        this.loadFromDisk();
    }
    display() {
        console.log(`Displaying ${this.filename}`);
    }
    loadFromDisk() {
        console.log(`Loading ${this.filename}`);
    }
}
//构造函数 constructor:
//接收一个 filename 参数,用于指定图片的文件名,并将其存储在 this.filename 中。
//调用 loadFromDisk 方法,模拟从磁盘加载图片的操作。这意味着只要创建 RealImage 对象,就会立即加载图片。
//display 方法:
//打印出正在显示指定文件名图片的信息,模拟图片显示的操作。
//loadFromDisk 方法:
//打印出正在加载指定文件名图片的信息,模拟从磁盘读取图片的过程。

// 代理对象类
class ProxyImage {
    constructor(filename) {
        this.filename = filename;
        this.realImage = null;
    }
    display() {
        if (!this.realImage) {
            this.realImage = new RealImage(this.filename);
            console.log("不存在,创建");
        }
        this.realImage.display();
    }
}

//构造函数 constructor:
//接收一个 filename 参数,存储图片的文件名。
//初始化 this.realImage 为 null,表示还没有创建真实的图片对象。
//display 方法:
//首先检查 this.realImage 是否为 null。如果为 null,说明还没有创建真实的图片对象,此时调用 new //RealImage(this.filename) 创建真实的图片对象,并将其赋值给 this.realImage,这一步会触发图片的加载操作。
//然后调用 this.realImage.display() 方法来显示图片。由于已经判断过是否需要加载图片,所以后续再次调用 display 方法时,不会重复加载图片。

// 使用示例
const proxyImage = new ProxyImage('test.jpg');
// 第一次调用,会加载图片
proxyImage.display(); 
// 第二次调用,不会再次加载图片
proxyImage.display(); 

//创建一个 ProxyImage 对象 proxyImage,传入图片文件名 'test.jpg'。此时并没有加载图片,只是创建了代理对象。
//第一次调用 proxyImage.display() 时,因为 this.realImage 为 null,所以会创建 RealImage 对象,触发图片加载操作,然后显示图片。
//第二次调用 proxyImage.display() 时,由于 this.realImage 已经不为 null,不会再次创建 RealImage 对象,也就不会再次加载图片,直接显示图片。

4、外观模式(Facade Pattern)

为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

解释:外观模式就像是一个前台接待员,当你进入一个复杂的系统时,不需要了解系统内部的各个子系统的具体操作,只需要和前台接待员沟通,前台接待员会帮你处理所有的事情。在编程中,就是为子系统中的一组接口提供一个一致的界面,使得子系统更加容易使用。

代码示范以及解释 

// 子系统类 1
class CPU {
    startup() {
        console.log('CPU startup');
    }
    shutdown() {
        console.log('CPU shutdown');
    }
}
//startup 方法:打印出 Memory startup,模拟内存启动的操作。
//shutdown 方法:打印出 Memory shutdown,模拟内存关闭的操作。
// 子系统类 2
class Memory {
    startup() {
        console.log('Memory startup');
    }
    shutdown() {
        console.log('Memory shutdown');
    }
}
// 方法同上,打印的语句不同而已

// 子系统类 3
class Disk {
    startup() {
        console.log('Disk startup');
    }
    shutdown() {
        console.log('Disk shutdown');
    }
}
// 方法同上,打印的语句不同而已

// 外观类
class ComputerFacade {
    constructor() {
        this.cpu = new CPU();
        this.memory = new Memory();
        this.disk = new Disk();
    }
    startup() {
        this.cpu.startup();
        this.memory.startup();
        this.disk.startup();
    }
    shutdown() {
        this.cpu.shutdown();
        this.memory.shutdown();
        this.disk.shutdown();
    }
}
//构造函数 constructor:
//创建 CPU、Memory 和 Disk 类的实例,并分别存储在 this.cpu、this.memory 和 this.disk 中。
//startup 方法:
//依次调用 CPU、Memory 和 Disk 实例的 startup 方法,实现计算机启动时各个组件的启动操作。
//shutdown 方法:
//依次调用 CPU、Memory 和 Disk 实例的 shutdown 方法,实现计算机关闭时各个组件的关闭操作。

// 使用示例
const computer = new ComputerFacade();
computer.startup(); 
// 输出:
// CPU startup
// Memory startup
// Disk startup

computer.shutdown(); 
// 输出:
// CPU shutdown
// Memory shutdown
// Disk shutdown

 三、小结

设计模式在编程当中还是挺重要的,优点包括但不限于:代码复用性更高、可维护性更强、可扩展性更好。有时间可以花点时间学习/复习一下,相信对我们的编程技术和编程思维会有很多进步~

到此,我们已经完成【设计模式】专栏的前两篇了,还有最后一篇的内容“行为型模式”,关注我,及时获取最新文章信息~

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

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

相关文章

LabVIEW太阳能制冷监控系统

在全球能源需求日益增长的背景下,太阳能作为一种无限再生能源,被广泛应用于各种能源系统中。本基于LabVIEW软件和STM32F105控制器的太阳能制冷监控系统的设计与实现,提供一个高效、经济的太阳能利用方案,以应对能源消耗的挑战。 项…

MambaMorph brain MR-CT

loss代码实现了几种用于医学图像配准(Registration)和分割(Segmentation)任务的损失函数,主要包括以下几种: NCC (Normalized Cross-Correlation): 功能: 计算局部归一化互相关损失,用于衡量两个图像之间的相似性。 应用场景: 通常用于图像配准任务,通过最大化图像之间…

单片机原理与运用

个人主页:java之路-CSDN博客(期待您的关注) 目录 一、走进单片机的世界 二、单片机是什么 (一)定义与本质 (二)与普通计算机的区别 三、单片机的工作原理深度剖析 (一)硬件组成及功能 &am…

一个根据输入内容过滤下拉选的组件

1.element的select自定义过滤不是很灵&#xff0c;使用了input和dropdown 组件 <template><div class"autocomplete-wrapper"><!-- 使用 el-input 组件 --><el-inputv-model"inputValue"input"handleInput"placeholder&q…

Linux | 进程相关概念(进程、进程状态、进程优先级、环境变量、进程地址空间)

文章目录 进程概念1、冯诺依曼体系结构2、进程2.1基本概念2.2描述进程-PCB2.3组织进程2.4查看进程2.5通过系统调用获取进程标识符2.6通过系统调用创建进程-fork初识fork の 头文件与返回值fork函数的调用逻辑和底层逻辑 3、进程状态3.1状态3.2进程状态查看命令3.2.1 ps命令3.2.…

sqli-labs靶场实录(四): Challenges

sqli-labs靶场实录: Challenges Less54确定字段数获取数据库名获取表名获取列名提取密钥值 Less55Less56Less57Less58爆库构造爆表构造爆列构造密钥提取构造 Less59Less60Less61Less62爆库构造 Less63Less64Less65免责声明&#xff1a; Less54 本关开始上难度了 可以看到此关仅…

Spring框架中都用到了哪些设计模式?

大家好&#xff0c;我是锋哥。今天分享关于【Spring框架中都用到了哪些设计模式&#xff1f;】面试题。希望对大家有帮助&#xff1b; Spring框架中都用到了哪些设计模式&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Spring框架中使用了大量的设计模…

ubuntu服务器部署

关闭欢迎消息 服务器安装好 ubuntu 系统后&#xff0c;进行终端登录&#xff0c;会显示出很多的欢迎消息 通过在用户的根目录下执行 touch .hushlogin 命令&#xff0c;再次登录终端就不会出现欢迎消息 修改hostname显示 修改 /etc/hostname 文件内容为主机名&#xff0c;保…

Centos7虚拟机安装及网络配置(二)

#二、centos7的网络配置-Nat模式 NAT模式也是VMware创建虚拟机的默认网络连接模式。使用NAT模式网络连接时&#xff0c;VMware会在主机上建立单独的专用网络&#xff0c;用以在主机和虚拟机之间相互通信。虚拟机向外部网络发送的请求数据"包裹"&#xff0c;都会交由…

关于视频去水印的一点尝试

一. 视频去水印的几种方法 1. 使用ffmpeg delogo滤镜 delogo 滤镜的原理是通过插值算法&#xff0c;用水印周围的像素填充水印的位置。 示例&#xff1a; ffmpeg -i input.mp4 -filter_complex "[0:v]delogox420:y920:w1070:h60" output.mp4 该命令表示通过滤镜…

twisted实现MMORPG 游戏数据库操作封装设计与实现

在设计 MMORPG&#xff08;大规模多人在线角色扮演游戏&#xff09;时&#xff0c;数据库系统是游戏架构中至关重要的一部分。数据库不仅承担了游戏中各种数据&#xff08;如玩家数据、物品数据、游戏世界状态等&#xff09;的存储和管理任务&#xff0c;还必须高效地支持并发访…

电脑端调用摄像头拍照:从基础到实现

文章目录 1. 了解navigator.mediaDevices.getUserMedia API2. 创建 HTML 结构3. 编写 JavaScript 代码3.1 打开摄像头3.2 拍照 4. 完整代码5. 测试6. 注意事项及部署 在现代 Web 开发中&#xff0c;调用摄像头进行拍照是一个常见的功能&#xff0c;尤其是在需要用户上传头像、进…

部署 DeepSeek R1各个版本所需硬件配置清单

DeepSeek-R1 通过其卓越的推理性能和灵活的训练机制&#xff0c;在 2025 年的春节期间受到了广泛关注。 DeepSeek-R1 是一款高性能的 AI 推理模型&#xff0c;主要通过强化学习技术来增强模型在复杂任务场景下的推理能力。 在本地部署 DeepSeek-R1 时&#xff0c;尤其是完整的…

算法18(力扣136)只出现一次的数字

1、问题 给你一个 非空 整数数组 nums&#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量额外空间。 2、示例 &#xff08;1&…

SiliconCloud 支持deepseek,送2000w token

SiliconCloud SiliconCloud 邀请奖励持续进行&#xff0c;2000 万 Tokens 送不停&#xff01; 邀请好友赚 2000 万 Tokens&#xff1a;每成功邀请一位新用户通过手机号码注册&#xff0c;您将获得 2000 万 Tokens&#xff1b;注册即送 2000 万 Tokens&#xff1a;受邀好友作为…

在nodejs中使用RabbitMQ(六)sharding消息分片

RabbitMQ 的分片插件&#xff08;rabbitmq_sharding&#xff09;允许将消息分布到多个队列中&#xff0c;这在消息量很大或处理速度要求高的情况下非常有用。分片功能通过将消息拆分到多个队列中来平衡负载&#xff0c;从而提升消息处理的吞吐量和可靠性。它能够在多个队列之间…

STM32 I2C通信协议说明

目录 背景 I2C协议 数据的有效性 I2C通信开始和停止条件 I2C数据传输 发送 响应 正常情况&#xff1a; 异常情况&#xff1a; 主机结束接收 写寄存器的标准流程 读寄存器的标准流程 仲裁机制 时钟同步 SDA线的仲裁 程序 背景 对单片机的三大通信中的I2C通信进…

Keysight E5071C (Agilent) 网络分析仪的特性和规格

安捷伦E5071C网络分析仪 Keysight E5071C网络分析仪 Keysight E5071C (Agilent) 网络分析仪的其他特性和规格包括&#xff1a; 宽动态范围&#xff1a;测试端口动态范围 > 123 dB&#xff08;典型值&#xff09; 快速测量速度&#xff1a;41 ms 全 2 端口校准&#xff0c;…

总结:如何在SpringBoot中使用https协议以及自签证书?

总结&#xff1a;如何在SpringBoot中使用https协议以及自签证书&#xff1f; 前提一&#xff1a;什么是http协议&#xff1f;前提二&#xff1a;什么是https协议&#xff1f;一生成自签证书二 将证书转换为PKCS12格式三 配置SpringBoot&#xff08;1&#xff09;修改配置文件&a…

基于SSM+uniapp的数学辅导小程序+LW示例参考

1.项目介绍 系统角色&#xff1a;管理员、普通用户功能模块&#xff1a;用户管理、学习中心、知识分类管理、学习周报管理、口算练习管理、试题管理、考试管理、错题本等技术选型&#xff1a;SSM&#xff0c;Vue&#xff08;后端管理web&#xff09;&#xff0c;uniapp等测试环…