【C++】设计模式

目录

  • 六大原则
  • 单例模式
  • 工厂模式
    • ①简单工厂模式
    • ②工厂方法模式
    • ③抽象工厂模式
  • 建造者模式
  • 代理模式

六大原则

单一责任原则(Single Responsibility Principle)

  • 类的职责应该单一,一个方法只做一件事,职责划分清晰,每次改动到最小单位的方法或类
  • 使用建议:两个完全不一样的功能不应该放在一个类中,一个类中应该是一组相关性很高的函数、数据的封装
  • 用例:网络聊天类(❌)应该分割成网络通信类 + 聊天类

开闭原则(Open Closed Principle)

  • 对扩展开放,对修改封闭(只添加新功能,不修改原有内容)
  • 使用建议:对软件实体的改动,最好用扩展而非修改的方式
  • 用例:超时卖货:商品价格—不是修改商品原来的价格,而是新增促销的价格

里氏替换原则(Liskov Substitution Principle)

  • 凡事父类能够出现的地方,子类就可以出现,而且替换为子类也不会出现任何的错误或者异常
  • 在继承类时,务必重写父类中的所有方法,尤其注意父类的protected方法,子类尽量不要暴露自己的public方法供外界调用
  • 使用建议:子类无比完全实现父类的方法,还子类可以有自己的个性,覆盖或者实现父类的方法时,输入的参数可以被放大,输出也可以缩小
  • 用例:跑步运动员类:会跑步, 子类长跑运动员-会跑步且擅长长跑,子类短跑运动员:会跑步且擅长短跑

依赖倒置原则(Dependence Inversion Principle)

  • 高层模块不应该依赖底层模块,两者都应该依赖其抽象,不可分隔的原子逻辑就是低层的模式,原子逻辑组装成的就是高层模块
  • 模块间依赖通过抽象(接口)发生,具体类之间不能直接依赖
  • 使用建议:每一个类都尽量有抽象类,任何类都不应该从具体类派生。尽量不要重写基类的方法。结合里氏替换原则使用
  • 用例:奔驰车司机 – 只能开奔驰,司机类:给什么车开什么车 : 开车的人 : 司机 – 依赖抽象

迪米特法则(Law of Demeter) 最少知道法则

  • 尽量减少对象之间的交互,从而减少类之间的耦合。一个对象应该对其他对象有最少的了解,对类的低耦合提出了明确的要求:
    只喝直接的朋友交流,朋友间也是有剧烈的。自己的就是自己的(如果一个方法放在本类中,既不增加类间关系,也不对本类造成负面影响,那就放置在本类中)
  • 用例:老师让班长点名,老师给班长名单,班长点名勾选,返回结果。老师只和班长交互,同学们只和班长交互

接口隔离原则

  • 客户端不应该依赖它不需要的接口,类间的依赖关系应该建立在最小的接口上
  • 使用建议:接口设计尽量精简单一,但是不要对外暴露没有啥意义的接口
  • 用例:修改密码,不应该提供用户信息接口,而是单一使用修改密码接口

总结:从整体上理解六大设计原则,可以简要概括为一句话,用抽象构建框架,用实现扩展细节,具体到每一条设计原则,则对应一条注意事项

单例模式

单例模式,顾名思义,就是只能由一个实例,那么我们就必须保证:该类不能被复制,该类不能被公开的创造。

单例模式又可以分为懒汉模式饿汉模式

懒汉模式:对于一个进程来说,我们只有在第一次使用的时候才去创建。优点是节省进程加载的时间。缺点是在第一次使用的时候才去创建。

饿汉模式:在代码转化为进程时,先于main函数之前就已经创建好了,不需要使用的时候再去创建。优点是可以直接使用,该资源早早创建出来,影响进程加载的速度。

饿汉单例模式:

构造、拷贝构造、赋值私有化,不能被显示构造。
静态单例对象的指针,在程序初始化时完成创建。
提供一个获取单例对象的静态对象,可以在任意位置获取单例对象
其生命周期与程序的生命周期是相同的。这个同样适用于多线程的情况,没有线程安全问题。

/* 饿汉单例模式 以空间换时间 */
class Singleton{
public:static Singleton* getInstance() { return _eton; }int getData() { return _data; }
private:Singleton(int data = 99) : _data(data){}~Singleton(){};Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;private:static Singleton* _eton;int _data;
};
Singleton* Singleton::_eton =new Singleton;

懒汉单例模式:

构造、拷贝构造、赋值私有化,不能被显示构造。
静态单例对象的指针,在程序初始化时完成创建。
提供一个获取单例对象的静态对象,可以在任意位置获取单例对象
其生命周期与程序的生命周期是相同的。懒汉单例模式会有线程安全问题,可以通过两种方式解决。

方法一:加锁 + 双检查 (C++98)

class Singleton
{
public://3、提供一个全局访问点获取单例对象static Singleton* GetInstance(){//双检查if (_inst == nullptr){_mtx.lock();if (_inst == nullptr){_inst = new Singleton;}_mtx.unlock();}return _inst;}
private://1、将构造函数设置为私有,并防拷贝Singleton(){}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;//2、提供一个指向单例对象的static指针static Singleton* _inst;static mutex _mtx; //互斥锁
};//在程序入口之前先将static指针初始化为空
Singleton* Singleton::_inst = nullptr;
mutex Singleton::_mtx; //初始化互斥锁

方法二:C++11后在静态函数中直接构造单例对象

/* 懒汉单例模式 懒加载 -- 延时加载思想 -- 一个对象用的时候再实例化 */
// 这里介绍<Effective C++> 作者提出的一种更加优雅简便的单例模式 Meyers Singleton int C++
// C++11后是线程安全的class Singleton{
public:static Singleton& getInstance() {static Singleton _eton;return _eton;}int getData() { return _data; }
private:Singleton(int data = 99) : _data(data){}~Singleton() {};Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;int _data;
};

工厂模式

工厂模式是一种创建型的设计模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们创建对象不会对上层暴露创建逻辑,而是通过使用一个共同结构来指向新创建的对象,因此实现创建-使用的分离。

①简单工厂模式

简单工厂模式:简单工厂模式实现需要由一个工厂对象通过类型决定创建出来的制定产品类的实例。假设有个工厂可以生产水果,当客户需要产品时明确告知工厂生产哪种水果,工厂需要接收用户提供的类别信息,当新增产品的时候,工厂内部取添加新产品的生产方式

class Fruit{
public:virtual void name() = 0;
private:   
};class Apple : public Fruit{
public:void name() override{std::cout << "I'm a apple" << std::endl;}
};class Banana : public Fruit{
public:void name() override {std::cout << "I'm a banana" << std::endl;}
};class FruitFactory {
public:static std::shared_ptr<Fruit> create(const std::string &name) {if (name == "苹果") {return std::make_shared<Apple>();} else {return std::make_shared<Banana>();}}
};int main() {std::shared_ptr<Fruit> fruit = FruitFactory::create("苹果");fruit->name();fruit = FruitFactory::create("香蕉");fruit->name();return 0;
}

这个模式的结构和管理产品对象的方式非常简单,但是它的扩展性非常差,当我们需要新增产品的时候,就需要去修改工厂类新增一个类型的产品创造逻辑,违背了开闭原则

②工厂方法模式

  工厂方法模式:在简单的工厂模式下新增了多个工厂,多个产品,每个产品对应一个工厂。假设现在有A、B两种产品,则开两个工厂,工厂A主要负责生产产品A,工厂B主要生产产品B,用户只要知道产品的工厂名,而不需要知道具体的产品信息,工厂不需要接收客户的产品类别,只负责生产产品。

  工厂方法模式不同于简单工厂模式的地方在于工厂方法模式把对象的创建过程放到里子类里。这样工厂父对象和产品父对象一样,可以是抽象类或者接口,只定义相应的规范或操作,不涉及具体的创建或实现细节。

/* 工厂方法模式 */
class Fruit{
public:virtual void name() = 0;
private:   
};class Apple : public Fruit{
public:void name() override{std::cout << "I'm a apple" << std::endl;}
};class Banana : public Fruit{
public:void name() override {std::cout << "I'm a banana" << std::endl;}
};
class FruitFactory {
public:virtual std::shared_ptr<Fruit> createFruit() = 0;
};class AppleFactory : public FruitFactory {
public:virtual std::shared_ptr<Fruit> createFruit() override {return std::make_shared<Apple>();}
};class BananaFactory : public FruitFactory {
public:virtual std::shared_ptr<Fruit> createFruit() override {return std::make_shared<Banana>();}
};int main() {std::shared_ptr<FruitFactory> ff(new AppleFactory());std::shared_ptr<Fruit> fruit1 = ff->createFruit();fruit1->name();ff.reset(new BananaFactory());std::shared_ptr<Fruit> fruit2 = ff->createFruit();fruit2->name();return 0;
}

工厂方法模式每次增减一个产品时,都需要增加一个具体的产品类和工厂类,这使得系统中类的个数成倍的增加,在一定程度上增加了系统的耦合度

③抽象工厂模式

抽象工厂模式:工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必增加系统的开销,此时我们可以考虑将一些相关的产品组成一个产品族(位于不同产品等级结构中功能相互关联的产品组成的家族),由于一个工厂统一生产,这就是抽象工厂模式的基本思想。

#include <iostream>
#include <memory>/* 简单工厂模式 */
class Fruit{
public:virtual void name() = 0;
private:   
};class Apple : public Fruit{
public:void name() override{std::cout << "I'm a apple" << std::endl;}
};class Banana : public Fruit{
public:void name() override {std::cout << "I'm a banana" << std::endl;}
};class Animal {public:virtual void name() = 0;
};class Lamp : public Animal {public:virtual void name() override { std::cout << "I'm a Lamp" << std::endl;}
};class Dog : public Animal {public:virtual void name() override {std::cout << "I'm  a dog" << std::endl;}
};class Factory {public: virtual std::shared_ptr<Fruit> getFruit(const std::string& name) = 0;virtual std::shared_ptr<Animal> getAnimal(const std::string& name) = 0;
};class FruitFactory : public Factory {public:virtual std::shared_ptr<Fruit> getFruit(const std::string& name) override{if (name == "苹果") {return std::make_shared<Apple>();} else {return std::make_shared<Banana>();}}virtual std::shared_ptr<Animal> getAnimal(const std::string& name) override{return std::shared_ptr<Animal>();}
};class AnimalFactory : public Factory {public:virtual std::shared_ptr<Fruit> getFruit(const std::string& name) override {return std::shared_ptr<Fruit>();}virtual std::shared_ptr<Animal> getAnimal(const std::string& name) override {if (name == "山羊") {return std::make_shared<Lamp>();} else {return std::make_shared<Dog>();}}
};class FactoryProducer {public: static std::shared_ptr<Factory> create(const std::string &name) {if (name == "水果") {return std::make_shared<FruitFactory>();} else {return std::make_shared<AnimalFactory>();}}
};int main() {std::shared_ptr<Factory> ff = FactoryProducer::create("水果");std::shared_ptr<Fruit> fruit = ff->getFruit("苹果");fruit->name();fruit = ff->getFruit("香蕉");fruit->name();ff = FactoryProducer::create("动物");std::shared_ptr<Animal> animal = ff->getAnimal("山羊");animal->name();animal = ff->getAnimal("小狗");animal->name();return 0;
}

抽象工厂模式适用于生产多个工厂系列产品衍生的设计模式,增加新的产品等级结构复杂,需要对原有系统进行较大修改,甚至需要修改抽象层代码,违背了开闭原则

建造者模式

建造者模式是一种创建型的设计模式,使用多个简单对象一步一步构建成一个复杂的对象,能够将一个复杂的对象的构建与它的表示分离,提供一种创建对象的最佳方式。主要用于解决对象的构建过于复杂的问题。

1.建造者(Builder)角色:给出一个抽象接口,以规范产品对象的各个组成成分的建造。一般而言,此接口独立于应用程序的商业逻辑。模式中直接创建产品对象的是具体建造者(Concrete Builder)角色。具体建造者类必须实现这个接口所要求的方法:一个是建造方法,另一个是结果返还方法。此时就是米线店的员工,按照收银员的要求的去准备具体的套餐,放入适当的米线,凉菜和饮料。

2.具体建造者(Concrete Builder)角色:担任这个角色的是于应用程序紧密相关的类,它们在应用程序调用下创建产品实例。这个角色主要完成的任务包括:实现Builder角色提供的接口,一步一步完成创建产品实例的过程。在建造过程完成后,提供产品的实例。是具体的做某个套餐的员工。

3.指导者(Director)角色:担任这个角色的类调用具体建造者角色以创建产品对象。导演者并没有产品类的具体知识,真正拥有产品类的具体知识的是具体建造者对象。是收银员,他知道我想要什么套餐,他会告诉里面的米线店员工去准备什么套餐。

4.产品(Product)角色:产品便是建造中的复杂对象。指导者角色是于客户端打交道的角色。导演者角色将客户端创建产品的请求划分为对各个零件的建造请求,再将这些请求委派给具体建造者角色。具体建造者角色是做具体建造工作的,但却不为客户端所知。就是最后的套餐,所有东西放到一起端过来。

建造者模式主要基于四个核心实现:

  • 抽象产品类
  • 具体产品类:一个具体的产品对象类
  • 抽象Builder类:创建一个产品对象所需要的各个零部件的抽象接口
  • 具体产品的Builder类:实现抽象接口,构建各个部件
  • 指挥者Director类:统一组建过程,提供给调用者使用,通过指挥者来获取产品
#include <iostream>
#include <string>
#include <memory>/* 通过MacBook的构造理解建造者模式*/class Computer{public:Computer(){};void setBoard(const std::string &board) { _board = board; }void setDisplay(const std::string &display) { _display = display; }virtual void setOs() = 0;void showParamaters() {std::string param = "Computer Paramaters: \n";param += "\tBoard: " + _board + "\n";param += "\tDispaly: " + _display + "\n";param += "\tOs: " + _os + "\n";std::cout << param << std::endl;}protected:std::string _board;std::string _display;std::string _os;
};
class MacBook : public Computer{public:virtual void setOs() override {_os = "Mac OS x12";}
};class Builder {public:virtual void buildBoard(const std::string &board) = 0;virtual void buildDisplay(const std::string &display) = 0;virtual void buildOs() = 0;virtual std::shared_ptr<Computer> build() = 0;
};class MacBookBuilder : public Builder{public:MacBookBuilder() : _computer(new MacBook()) {}void buildBoard(const std::string& board) {_computer->setBoard(board);}void buildDisplay(const std::string& display) {_computer->setDisplay(display);}void buildOs() {_computer->setOs();}std::shared_ptr<Computer> build() {return _computer;}private:std::shared_ptr<Computer> _computer;
};class Director {public:Director(Builder* builder) : _builder(builder) {}void construct(const std::string& board, const std::string& display) {_builder->buildBoard(board);_builder->buildDisplay(display);_builder->buildOs();}private:std::shared_ptr<Builder> _builder;
};int main() {Builder * builder = new MacBookBuilder();std::unique_ptr<Director> director(new Director(builder));director->construct("华硕主板", "三星显示器");std::shared_ptr<Computer> computer = builder->build();computer->showParamaters();return 0;
}

代理模式

代理模式指的是代理控制对其他对象的访问,也就是代理对象控制对原对象的引用。在某些情况下,一个对象不适合或者不能直接被引用访问,而代理对象可以在客户端和目标对象之间起到中介作用

代理模式的结构包括一个是真正的你要访问的目标对象(目标类)、一个是代理对象。目标对象与代理对象实现同一个接口,先访问代理类再通过代理类访问目标对象。代理模式一般分为静态代理、动态代理

  • 静态代理指的是,在编译时就已经确定好了代理类和被代理类的关系。也就是说,在编译时已经确定了代理要代理的是哪一个被代理类
  • 动态代理指的是,在运行时才动态生成代理类,并将其与被代理类绑定。这意味着,在运行时才能确定代理类要代理的是哪个被代理类

以租房为例,租客租房,中间经过房屋中介向房东租房,使用代理模式实现

#include <iostream>/* 代理模式 */
class RentHouse {public:virtual void rentHouse() = 0;
};class Landlord : public RentHouse {public:void rentHouse() {std::cout << "房子租出去了" << std::endl;}
};class Intermediary : public RentHouse {public:void rentHouse() {std::cout << "发布招租启事" << std::endl;std::cout << "带人看房" << std::endl;_landload.rentHouse();std::cout << "负责租后维修" << std::endl;}private:Landlord _landload;
};int main() {Intermediary intermediary;intermediary.rentHouse();return 0;
}

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

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

相关文章

在排序数组中查找元素的第一个和最后一个位置

给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。 示例 1&#xff1a…

解决前端二进制流下载的文件(例如:excel)打不开的问题

1. 现在后端请求数据后&#xff0c;返回了一个二进制的数据&#xff0c;我们要把它下载下来。 这是响应的数据&#xff1a; 2. 这是调用接口的地方&#xff1a; uploadOk(){if(this.files.length 0){return this.$Message.warning("请选择上传文件&#xff01;&#xff…

泛洪攻击(Flood Attack)

执行网络攻击&#xff0c;包括 Ping 泛洪攻击&#xff0c;在绝大多数国家/地区都是非法的&#xff0c;它违反了计算机犯罪法律和条例。执行或尝试执行这样的攻击通常会导致法律后果&#xff0c;包括罚款和监禁。因此&#xff0c;这些信息仅供理论学习和了解&#xff0c;绝不应用…

Multi Label Classification with Missing Labels(MLML)的几种loss设计

多标签学习这个方向问题比较多&#xff0c;可以参考多标签学习的新趋势&#xff08;2021 Survey TPAMI&#xff09; 和 部分标签学习和缺失标签学习到底什么异同&#xff1f; 这两篇偏综述性质的解释。本文重点解释下面几个重点问题&#xff1a; Multi Label Classification w…

第十五章 类和对象——友元

生活中你的家有客厅(Public)&#xff0c;有你的卧室(Private) 客厅所有来的客人都可以进去&#xff0c;但是你的卧室是私有的&#xff0c;也就是说只有你能进去 但是呢&#xff0c;你也可以允许你的好闺蜜好基友进去。 在程序里&#xff0c;有些私有属性 也想让类外特殊的一些…

【物联网】STM32的中断机制不清楚?看这篇文章就足够了

在嵌入式系统中&#xff0c;中断是一种重要的机制&#xff0c;用于处理来自外部设备的异步事件。STM32系列微控制器提供了强大的中断控制器&#xff0c;可以方便地处理各种外部中断和内部中断。本文将详细介绍STM32中断的结构和使用方法。 文章目录 1. 什么叫中断2. 中断优先级…

Jmeter基础篇

1.性能测试指标 【虚拟用户数】&#xff1a;线程用户 【并发数】&#xff1a;指在某一时间&#xff0c;一定数量的虚拟用户同时对系统的某个功能进行交互&#xff0c;一般通过集合点实现 【事务】:事务代表一个完整的功能&#xff0c;一个接口可以是事务&#xff0c;多个接口…

linux——进程间通信——管道

✅<1>主页&#xff1a;&#xff1a;我的代码爱吃辣 &#x1f4c3;<2>知识讲解&#xff1a;Linux——进程间通信——管道通信 ☂️<3>开发环境&#xff1a;Centos7 &#x1f4ac;<4>前言&#xff1a;进程间通信&#xff08;InterProcess Communication&…

数据结构与算法系列-二分查找

二分查找 什么是二分查找&#xff1f; 二分查找是一种针对有序集合&#xff0c;每次将要查找的区间缩小一半&#xff0c;直到找到查找元素&#xff0c;或区间被缩小为0。 如何实现二分查找&#xff1f; 实现有3个注意点&#xff1a; 终止条件是 low < high 2.求中点的算…

Matlab杂项记录

文章目录 其他do nothing command in matlab代码格式化在同一个m文件中写多个独立的功能函数改变启动时的默认文件夹博文链接 table使用 其他 do nothing command in matlab disp() % Does nothing but allows developer to set a breakpoint here.代码格式化 Matlab编辑器具…

强化学习实践(一)Gym介绍

学了一段时间强化学习的理论&#xff0c;近期准备进行一些算法实践。应用算法的前提是要创建一个合适的仿真环境&#xff0c;目前Openai的Gym(https://gym.openai.com) 是主流的强化学习实验环境库。 Gym已经集成许多开发好的环境&#xff0c;让RL的研究者们可以直接上手使用&a…

Python的函数

近期遇到了一个没怎么看懂的Python函数的形式。 def twoSum(self, nums: List[int], target: int) -> List[int]: 后来上网查了资料。

(二)激光线扫描-相机标定

1. 何为相机标定? 当相机拍摄照片时,我们看到的图像通常与我们实际看到的不完全相同。这是由相机镜头引起的,而且发生的频率比我们想象的要高。 这种图像的改变就是我们所说的畸变。一般来说,畸变是指直线在图像中出现弯曲或弯曲。 这种畸变我们可以通过相机标定来进行解…

Hudi SQL DDL

本文介绍Hudi在 Spark 和 Flink 中使用SQL创建和更改表的支持。 1.Spark SQL 创建hudi表 1.1 创建非分区表 使用标准CREATE TABLE语法创建表,该语法支持分区和传递表属性。 CREATE TABLE [IF NOT EXISTS] [db_name.]table_name[(col_name data_type [COMMENT col_comment], ..…

阿里云服务器地域和可用区查询表_地域可用区大全

阿里云服务器地域和可用区有哪些&#xff1f;阿里云服务器地域节点遍布全球29个地域、88个可用区&#xff0c;包括中国大陆、中国香港、日本、美国、新加坡、孟买、泰国、首尔、迪拜等地域&#xff0c;同一个地域下有多个可用区可以选择&#xff0c;阿里云服务器网分享2023新版…

【AntDesign】封装全局异常处理-全局拦截器

[toc] 场景 本文前端用的是阿里的Ant-Design框架&#xff0c;其他框架也有全局拦截器&#xff0c;思路是相同&#xff0c;具体实现自行百度下吧 因为每次都需要调接口&#xff0c;都需要单独处理异常情况&#xff08;code !0&#xff09;&#xff0c;因此前端需要对后端返回的…

【Linux】Linux常用命令—文件管理(上)

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

windows11 安装Nodejs

一、介绍 NPM 全称 Node Package Manager&#xff0c;它是 JavaScript 的包管理工具, 并且是 Node.js 平台的默认包管理工具。通 过 NPM 可以安装、共享、分发代码,管理项目依赖关系。 可从NPM服务器下载别人编写的第三方包到本地使用。可从NPM服务器下载并安装别人编写的命令…

java获取字符串集合中每个字符并且组成一个新的集合实现

直接怼代码&#xff0c;刚好碰到了这种需求&#xff0c;也是想了可久&#xff0c;其实想想也还是挺简单的 public static void main(String[] args) { // 原始字符串集合 List<String> originalList new ArrayList<>(); originalList.add("Hello"); …

基于python的网络爬虫搜索引擎的设计

项目介绍 随着互联网的飞速发展&#xff0c;web已经成为人们主要的检索&#xff0c;和发布的主要平台&#xff0c;在海量的数据中如何快速&#xff0c;准确的找到用户所需要的信息成为人们当前所需求的&#xff0c;而网络爬虫就是为了满足这一需要而产生的研究领域。在现实中我…