23种设计模式-7种结构模式
结构型模式简述
把类或对象结合在一起形成一个更大的结构。
装饰器模式:动态的给对象添加新的功能。
代理模式:为其它对象提供一个代理以便控制这个对象的访问。
桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。
适配器模式:将一个类的方法接口转换成客户希望的另一个接口。
组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。
外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
享元模式:通过共享技术来有效的支持大量细粒度的对象。
模式C++代码实现
桥接模式
桥接模式(Bridge Pattern)是一种结构性设计模式,用于将抽象部分与其实现部分分离,使它们可以独立地变化。桥接模式通过将多维度的类层次结构分为两个独立的继承层次结构,从而提高了系统的可扩展性和灵活性。
以下是一个使用C++实现桥接模式的简单示例,假设我们要实现不同类型的电视和遥控器的桥接模式:
#include <iostream>// 实现部分
class TV {
public:virtual void turnOn() = 0;virtual void turnOff() = 0;
};class SonyTV : public TV {
public:void turnOn() override {std::cout << "Sony TV is turned on" << std::endl;}void turnOff() override {std::cout << "Sony TV is turned off" << std::endl;}
};class LGTV : public TV {
public:void turnOn() override {std::cout << "LG TV is turned on" << std::endl;}void turnOff() override {std::cout << "LG TV is turned off" << std::endl;}
};// 抽象部分
class RemoteControl {
protected:TV* tv;public:RemoteControl(TV* tv) : tv(tv) {}virtual void turnOn() {tv->turnOn();}virtual void turnOff() {tv->turnOff();}
};class BasicRemoteControl : public RemoteControl {
public:BasicRemoteControl(TV* tv) : RemoteControl(tv) {}void turnOn() override {std::cout << "Basic Remote: ";RemoteControl::turnOn();}void turnOff() override {std::cout << "Basic Remote: ";RemoteControl::turnOff();}
};class AdvancedRemoteControl : public RemoteControl {
public:AdvancedRemoteControl(TV* tv) : RemoteControl(tv) {}void mute() {std::cout << "Advanced Remote: Muted" << std::endl;}
};int main() {TV* sonyTV = new SonyTV();TV* lgTV = new LGTV();RemoteControl* basicRemoteSony = new BasicRemoteControl(sonyTV);RemoteControl* advancedRemoteLG = new AdvancedRemoteControl(lgTV);basicRemoteSony->turnOn();basicRemoteSony->turnOff();advancedRemoteLG->turnOn();advancedRemoteLG->mute();advancedRemoteLG->turnOff();delete sonyTV;delete lgTV;delete basicRemoteSony;delete advancedRemoteLG;return 0;
}
在这个示例中,TV 类表示实现部分,SonyTV 和 LGTV 是其具体实现。RemoteControl 类表示抽象部分,BasicRemoteControl 和 AdvancedRemoteControl 是其具体实现。通过使用桥接模式,我们可以轻松地将不同的遥控器与不同的电视组合,实现了抽象部分和实现部分的解耦。
装饰模式
装饰模式(Decorator Pattern)是一种结构性设计模式,允许在不改变已有对象的结构的情况下,动态地将功能添加到对象上。在装饰模式中,通过创建装饰器类,将装饰器类与被装饰的类组合,从而实现功能的叠加和扩展。
以下是一个使用C++实现装饰模式的简单示例,假设我们要实现不同类型的咖啡以及可以添加调料的功能:
#include <iostream>
#include <string>// 基础咖啡类(被装饰的类)
class Coffee {
public:virtual std::string getDescription() const {return "Basic Coffee";}virtual double cost() const {return 1.0;}
};// 调料装饰器类
class CoffeeDecorator : public Coffee {
protected:Coffee* coffee;public:CoffeeDecorator(Coffee* coffee) : coffee(coffee) {}std::string getDescription() const override {return coffee->getDescription();}double cost() const override {return coffee->cost();}
};// 具体的调料装饰器类
class MilkDecorator : public CoffeeDecorator {
public:MilkDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}std::string getDescription() const override {return coffee->getDescription() + ", Milk";}double cost() const override {return coffee->cost() + 0.5;}
};class SugarDecorator : public CoffeeDecorator {
public:SugarDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}std::string getDescription() const override {return coffee->getDescription() + ", Sugar";}double cost() const override {return coffee->cost() + 0.2;}
};int main() {Coffee* basicCoffee = new Coffee();Coffee* milkCoffee = new MilkDecorator(basicCoffee);Coffee* milkSugarCoffee = new SugarDecorator(milkCoffee);std::cout << "Description: " << milkSugarCoffee->getDescription() << std::endl;std::cout << "Cost: $" << milkSugarCoffee->cost() << std::endl;delete basicCoffee;delete milkCoffee;delete milkSugarCoffee;return 0;
}
在这个示例中,Coffee 类表示被装饰的基础咖啡,CoffeeDecorator 类表示调料装饰器。具体的调料装饰器类如 MilkDecorator 和 SugarDecorator 继承自 CoffeeDecorator,并通过重写 getDescription 和 cost 方法来添加调料的描述和价格。通过组合不同的装饰器,我们可以在运行时动态地为咖啡添加不同的调料。
适配器模式
适配器模式(Adapter Pattern)是一种结构性设计模式,它允许将不兼容的接口转换成客户端期望的接口。适配器模式通常用于连接两个不兼容的接口,使它们可以一起工作。
以下是一个使用C++实现适配器模式的简单示例,假设我们有一个已有的音频播放器接口和一个新的MP3播放器类,我们需要将MP3播放器接口适配成音频播放器接口。
#include <iostream>
#include <string>// 旧的音频播放器接口
class AudioPlayer {
public:virtual void playAudio(const std::string& audioType, const std::string& fileName) = 0;
};// 新的MP3播放器类
class Mp3Player {
public:void playMp3(const std::string& fileName) {std::cout << "Playing MP3 file: " << fileName << std::endl;}
};// 适配器类,将Mp3Player适配成AudioPlayer接口
class Mp3Adapter : public AudioPlayer {
private:Mp3Player mp3Player;public:void playAudio(const std::string& audioType, const std::string& fileName) override {if (audioType == "mp3") {mp3Player.playMp3(fileName);}}
};int main() {AudioPlayer* audioPlayer = new Mp3Adapter();audioPlayer->playAudio("mp3", "song.mp3");audioPlayer->playAudio("wma", "song.wma"); // 这不会播放,因为适配器只支持MP3delete audioPlayer;return 0;
}
在这个示例中,AudioPlayer 是旧的音频播放器接口,Mp3Player 是新的MP3播放器类,Mp3Adapter 是适配器类,它继承自 AudioPlayer 并包含一个 Mp3Player 对象。适配器类的 playAudio 方法将传入的 audioType 参数与适配的格式进行比较,如果是 “mp3” 格式,就调用 Mp3Player 的方法来播放。
通过适配器模式,我们可以在不改变已有代码的情况下,将不兼容的MP3播放器接口适配成了音频播放器接口,从而让它们可以一起工作。
外观模式
外观模式(Facade Pattern)是一种结构性设计模式,它为复杂的子系统提供一个统一的接口,使得外部客户端可以更简单地使用该子系统,而不需要了解其内部的复杂逻辑。
以下是一个使用C++实现外观模式的简单示例,假设我们有一个复杂的电子设备系统,包括电视、音响和灯光等组件,我们可以使用外观模式来创建一个统一的接口以简化客户端的操作。
#include <iostream>// 子系统:电视
class TV {
public:void turnOn() {std::cout << "TV is on" << std::endl;}void turnOff() {std::cout << "TV is off" << std::endl;}
};// 子系统:音响
class Stereo {
public:void turnOn() {std::cout << "Stereo is on" << std::endl;}void turnOff() {std::cout << "Stereo is off" << std::endl;}
};// 子系统:灯光
class Lights {
public:void turnOn() {std::cout << "Lights are on" << std::endl;}void turnOff() {std::cout << "Lights are off" << std::endl;}
};// 外观类:家庭影院
class HomeTheaterFacade {
private:TV tv;Stereo stereo;Lights lights;public:void watchMovie() {std::cout << "Get ready to watch a movie!" << std::endl;tv.turnOn();stereo.turnOn();lights.turnOff();}void endMovie() {std::cout << "Movie is over, shutting down..." << std::endl;tv.turnOff();stereo.turnOff();lights.turnOn();}
};int main() {HomeTheaterFacade homeTheater;homeTheater.watchMovie();std::cout << "----------------------" << std::endl;homeTheater.endMovie();return 0;
}
在这个示例中,TV、Stereo 和 Lights 分别表示不同的电子设备子系统。HomeTheaterFacade 是外观类,它封装了对各个子系统的操作,提供了 watchMovie 和 endMovie 方法来统一操作电视、音响和灯光等组件,从而让客户端更简单地操作整个家庭影院。
通过外观模式,我们将复杂的子系统封装成一个简单的接口,使得客户端不需要了解子系统的复杂性,只需通过外观类来操作整个系统。
组合模式
组合模式(Composite Pattern)是一种结构性设计模式,它允许将对象组合成树形结构,以表示“部分-整体”的层次结构。组合模式使得客户端可以统一处理单个对象和对象组合,而不需要区分它们的具体类型。
以下是一个使用C++实现组合模式的简单示例,假设我们要创建一个文件系统的层次结构,包括文件和文件夹。文件夹可以包含文件和其他文件夹,形成一个树形结构。
#include <iostream>
#include <vector>// 组件抽象基类
class FileSystemComponent {
public:virtual void display() const = 0;
};// 文件类
class File : public FileSystemComponent {
private:std::string name;public:File(const std::string& n) : name(n) {}void display() const override {std::cout << "File: " << name << std::endl;}
};// 文件夹类
class Folder : public FileSystemComponent {
private:std::string name;std::vector<FileSystemComponent*> components;public:Folder(const std::string& n) : name(n) {}void addComponent(FileSystemComponent* component) {components.push_back(component);}void display() const override {std::cout << "Folder: " << name << std::endl;for (const auto& component : components) {component->display();}}
};int main() {File file1("file1.txt");File file2("file2.txt");File file3("file3.txt");Folder folder1("folder1");folder1.addComponent(&file1);folder1.addComponent(&file2);Folder folder2("folder2");folder2.addComponent(&file3);Folder root("root");root.addComponent(&folder1);root.addComponent(&folder2);root.display();return 0;
}
在这个示例中,FileSystemComponent 是组件的抽象基类,包括了 File 和 Folder 类。File 表示文件,Folder 表示文件夹,文件夹可以包含文件和其他文件夹。通过使用组合模式,我们可以将文件和文件夹组合成一个层次结构,使得客户端可以统一地处理不同层次的组件。
通过组合模式,我们可以轻松地创建复杂的层次结构,同时可以使用统一的接口来操作整个结构。这种模式在处理递归结构时特别有用,例如文件系统、组织架构等。
代理模式
代理模式(Proxy Pattern)是一种结构性设计模式,它提供了一个代理对象,用于控制对原始对象的访问。代理对象可以作为原始对象的替代,用于控制、管理或增强原始对象的访问。
以下是一个使用C++实现代理模式的简单示例,假设我们有一个图片加载器,可以从磁盘加载图片并显示。我们可以使用代理模式创建一个代理图片加载器,用于控制图片的加载和显示。
#include <iostream>
#include <string>// 抽象主题
class Image {
public:virtual void display() = 0;
};// 真实主题
class RealImage : public Image {
private:std::string filename;public:RealImage(const std::string& file) : filename(file) {loadFromDisk();}void display() override {std::cout << "Displaying image: " << filename << std::endl;}void loadFromDisk() {std::cout << "Loading image: " << filename << std::endl;}
};// 代理类
class ProxyImage : public Image {
private:RealImage* realImage;std::string filename;public:ProxyImage(const std::string& file) : filename(file), realImage(nullptr) {}void display() override {if (realImage == nullptr) {realImage = new RealImage(filename);}realImage->display();}
};int main() {Image* image = new ProxyImage("image.jpg");// 图片未加载,调用display会加载并显示image->display();// 图片已加载,直接显示image->display();return 0;
}
在这个示例中,Image 是抽象主题接口,定义了图片的显示方法。RealImage 是真实主题类,实现了图片加载和显示的功能。ProxyImage 是代理类,持有一个真实主题的实例,在需要时创建并控制真实主题的访问。
通过代理模式,我们可以延迟真实主题的创建和加载,只有在需要时才会真正地创建和加载对象。代理可以用于实现懒加载、权限控制、缓存等功能,从而对原始对象的访问进行了增强或控制。
享元模式
享元模式(Flyweight Pattern)是一种结构性设计模式,旨在通过共享对象来最大程度地减少内存使用和对象创建的开销。它适用于存在大量相似对象的场景,通过共享这些相似部分,可以有效减少内存占用。
以下是一个使用C++实现享元模式的简单示例,假设我们要创建一个文字编辑器,需要创建大量的字符对象。我们可以使用享元模式来共享相同字符的实例,以减少内存使用。
#include <iostream>
#include <unordered_map>// 字符类,享元类
class Character {
private:char symbol;public:Character(char c) : symbol(c) {}char getSymbol() const {return symbol;}void display(int pointSize) {std::cout << "Character: " << symbol << " Point size: " << pointSize << std::endl;}
};// 字符工厂,负责创建和管理字符实例
class CharacterFactory {
private:std::unordered_map<char, Character*> characters;public:Character* getCharacter(char c) {if (characters.find(c) == characters.end()) {characters[c] = new Character(c);}return characters[c];}
};int main() {CharacterFactory characterFactory;// 创建并显示字符实例Character* c1 = characterFactory.getCharacter('A');c1->display(12);Character* c2 = characterFactory.getCharacter('B');c2->display(16);// 再次获取相同字符实例Character* c3 = characterFactory.getCharacter('A');c3->display(20);// 释放资源delete c1;delete c2;return 0;
}
在这个示例中,Character 是享元类,表示一个字符对象。CharacterFactory 是字符工厂类,负责创建和管理字符实例。通过使用享元模式,我们可以共享相同字符的实例,从而减少了内存的使用。
享元模式的关键是将对象的共享和非共享状态分离,共享状态可以被多个对象共享,非共享状态可以通过参数传递给对象。这样可以在一定程度上减少对象的创建和内存占用。
相关文章:
23种设计模式-7种结构模式
结构型模式简述 把类或对象结合在一起形成一个更大的结构。 装饰器模式:动态的给对象添加新的功能。 代理模式:为其它对象提供一个代理以便控制这个对象的访问。 桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变…...
大数据Flink(六十七):SQL Table 简介及运行环境
文章目录 SQL & Table 简介及运行环境 一、简介 二、案例...
WPF使用依赖注入
现在依赖注入在.Net里面已经普及,自己常写一些简单的demo倒是无所谓,但偶尔写一点正式的工程,也免不了要使用一下,于是总结了一下在WPF里面使用依赖注入。 在写简单Demo时候,通常是在MainWindow的构造函数里面直接做初…...
玩转科技|了解AI平台桌面客户端—ChatBox
目录 前言 特性 编辑 为什么需要 ChatBox? ChatGPT Plus 平替? 下载 支持系统 功能图 使用教程 感受 展示 前言 今天小编又来了,推荐给大家一款开源的OpenAI API桌面客户端ChatBox,它支持 Windows、Mac 和 Linux。…...
visual studio 2022.NET Core 3.1 未显示在目标框架下拉列表中
问题描述 在Visual Studio 2022我已经安装了 .NET core 3.1 并验证可以运行 .NET core 3.1 应用程序,但当创建一个新项目时,目标框架的下拉列表只允许 .NET 6.0和7.0。而我在之前用的 Visual Studio 2019,可以正确地添加 .NET 核心项目。 …...
人工智能项目集合推荐(数据集 模型训练 C++和Android部署)
人工智能项目集合推荐(数据集 模型训练 C和Android部署) 目录 人工智能项目集合推荐(数据集 模型训练 C和Android部署) 1.三维重建项目集合 ★双目三维重建 ★结构光三维重建 2.AI CV项目集合 ★人脸检测和人体检测 ★人体姿态估计(人体关键点检测) ★头部朝向估计 …...
C# 服务HTTPS 对 请求被中止: 未能创建 SSL/TLS 安全通道报错
1.如果windows支持HTTPS的TLS协议,则可以直接跳过 (Tls12) [WebMethod(Description "获取HttpsPost加密服务.")] public string HTTPSPOST(String input,String sUrl) { Log.Add("ReceiveNotice", &qu…...
二级MySQL(七)——表格数据修改
1、修改表格中部分数据 将表格某一行的数据修改,这里用的UPDATE语句: UPDATE tb_student SET studentName 黄涛,native湖北,nation汉 WHERE studentNo 2014210103; 结果: 2、修改表格某一列全部数据 比如性别全部设置为‘女’ UPDATE…...
【日常积累】Linux下sftp搭建
概述 SFTP是Secure File Transfer Protocol的缩写,是安全文件传送协议。可以为传输文件提供一种安全的加密方法。跟ftp几乎语法功能一样。 SFTP是SSH的一部分,是一种传输档案至Blogger伺服器的安全方式。它本身没有单独的守护进程,必须使用s…...
【深入浅出C#】章节 9: C#高级主题:多线程编程和并发处理
多线程编程和并发处理的重要性和背景 在计算机科学领域,多线程编程和并发处理是一种关键技术,旨在充分利用现代计算机系统中的多核处理器和多任务能力。随着计算机硬件的发展,单一的中央处理单元(CPU)已经不再是主流&a…...
Windows Server服务器安全加固基线配置
一、账户管理、认证授权 一、账户 1、管理缺省账户 安全基线项说明:对于管理员账号,要求更改缺省账户名称;禁用Guest(来宾)账户。 操作步骤:进入控制面板-->管理工具-->计算机管理,在系统工具-->本地用户和组…...
基于NXP i.MX 6ULL核心板的物联网模块开发案例(4)
目录 5 4G模块测试 5.1 网络功能测试 5.2 短信功能测试 5.3 通话功能测试 5.4 GPS定位功能测试 5.5 程序编译 前言 本文主要介绍基于创龙科技TLIMX6U-EVM评估板的物联网模块开发案例,适用开发环境: Windows开发环境:Windows 7 64bit、Windows 10 64bit 虚拟机:VMware15.…...
英语——强调
强调句是英语中常用的一个重点句型,其基本结构是:It+be+被强调部分+that+句子其余部分。 第一节 强调句的基本用法 一、被强调的句子成分 在强调句型中,能够被强调的句子成分通常为主语、宾语、状语等,不能用来强调谓语动词、表语、补语、让步状语、条件状语等。当被强调…...
全流程R语言Meta分析核心技术教程
详情点击链接:全流程R语言Meta分析核心技术教程 一,Meta分析的选题与检索 1、Meta分析的选题与文献检索 1)什么是Meta分析? 2)Meta分析的选题策略 3)精确检索策略,如何检索全、检索准 4)文献的管理与清洗,如何制定文…...
【C++精华铺】9.STL string
目录 1. string类的优势 2. string类的常用接口 2.1 常用构造 1. 空串构造:string(); 2. C串构造:string(const char* s); 3. 拷贝构造:string(const string& str); 4. 字符填充构造:string(size_t n, char c); 5. 迭代…...
【PACS】医学影像管理系统源码带三维重建后处理技术
PACS系统,意为影像归档和通信系统。它是应用在医院影像科室的系统,主要的任务就是把日常产生的各种医学影像(包括核磁,CT,超声,各种X光机,各种红外仪、显微仪等设备产生的图像)通过各…...
从0开始学go 第一天
今天是开始学go的第x天,前些日子看了看语言,今天找一个web开发来跟着学,记录一下遇到的问题,方便以后复习查阅。 视频看的是https://www.bilibili.com/video/BV1gJ411p7xC?p3&vd_sourceab5bdbd04f4142027c66d604d5285204 视…...
Spring Cloud Nacos详解
目录 1、Spring Cloud Nacos详细介绍2、Spring Cloud Nacos具体案列 Spring Cloud Nacos 是一个由阿里巴巴集团开发的开源分布式系统服务发现、配置管理和服务管理的平台。Nacos 支持多种服务发现方式,包括 DNS 方式、HTTP 和 RPC 方式,同时提供了灵活的…...
2023谷歌开发者大会直播大纲「初稿」
听人劝、吃饱饭,奉劝各位小伙伴,不要订阅该文所属专栏。 作者:不渴望力量的哈士奇(哈哥),十余年工作经验, 跨域学习者,从事过全栈研发、产品经理等工作,现任研发部门 CTO 。荣誉:2022年度博客之星Top4、博客专家认证、全栈领域优质创作者、新星计划导师,“星荐官共赢计…...
react import 引用失效 node_modules/@types/react/index.d.ts not a module.ts
问题描述 react ts的项目,正常使用vs code打开, 先运行 npm install 安装依赖过后 结果所有的react引用依旧标红,如下图所示: 点击红线 show problem(查看问题),提示node_modules/types/react/index.d.ts not a mod…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...
C++_哈希表
本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说,直接开始吧! 一、基础概念 1. 哈希核心思想: 哈希函数的作用:通过此函数建立一个Key与存储位置之间的映射关系。理想目标:实现…...
DAY 45 超大力王爱学Python
来自超大力王的友情提示:在用tensordoard的时候一定一定要用绝对位置,例如:tensorboard --logdir"D:\代码\archive (1)\runs\cifar10_mlp_experiment_2" 不然读取不了数据 知识点回顾: tensorboard的发展历史和原理tens…...
