C++设计模式

UML统一建模语言

  1. 基于面向对象的可视化建模语言。本质其实是一些图形
  2. UML中的关系:
    泛化关系:比如类的继承
    关联关系:包含和被包含的关系,比如类作为另一个类的成员
    依赖关系:比如类传参进去
    实现关系:比如微信支付和支付

设计模式

基本概念

  1. 设计模式是在特定环境下人们解决某类重复出现的问题的一套成功或有限的方案
  2. 在代码里面就是充分提高代码 复用性, 指导代码的思路方法,相当于内功,与具体语言无关
  3. 设计模式的基础就是 多态

面向对象设计原则

  1. 如何提高系统的可维护性和可复用性是面向对象设计的核心问题
  2. 大原则:高内聚、低耦合
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

开闭原则

  1. 对扩张开放,对修改关闭,增加功能通过增加代码实现,而不是通过修改原来的代码,少改代码,减少出错可能
  2. 例子就是比如写一个计算器类,通过抽象基类,再用子类继承,重写实现,这样通过父类指针就可以指向不同的子类实现,达到一个接口,多种形态的效果,以后增加功能只需要再增加对应的代码就好,不用修改原来的代码。

迪米特原则

  1. 最少知识原则,写了一个类,应该尽可能少的暴露内部给其他人,只提供它们需要的接口就行
  2. 例子就是我想写一个买房子的功能,有类里面有各种各样房子的信息,房子有各种特征,实现这个功能时候,不是说我来一个个去判断他这些房子是否满足我要求,而是写一个接口,只需要传入我的要求,他就给我返回最合适的选择

合成复用原则

  1. 就是一个类想要另外一个类的方法,可以通过继承实现, 也可以通过组合实现
  2. 少用继承,优先使用组合。

依赖倒转原则

传统的:
在这里插入图片描述
依赖倒转
在这里插入图片描述

简单工厂模式

在这里插入图片描述

  1. 就是创建一个专门生产对象的类,创建类的时候调用它的方法。在工厂中判断,需要生产什么类就生产啥,这样用户不用管怎么创建。
  2. 这个模式应用有限,有缺陷,不符合开闭原则,要跟新功能的时候还需要去改代码,一个工厂需要做到功能太多。
  3. 只适合在那种,比如创建一个对象步骤比较麻烦,但是又需要经常创建,就把这个事情分给工厂做,不让用户管对象是咋创建的。
    在这里插入图片描述

工厂方法模式

在这里插入图片描述

  1. 这个模式是简单工厂的改进,可以符合开闭原则了,但是类变多了,维护成本增加了,一个产品就得对应一个类。
  2. 就是用一个工厂创建一个类
    在这里插入图片描述

抽象工厂模式

在这里插入图片描述

  1. 不同国家的水果,构成产品等级, 一个国家的水果,构成产品族
  2. 抽象工厂模式,对于增加产品族,是方便的,页满足开闭原则,但是如果需要增加产品等级,那就不符合开闭原则了

单例模式

  1. 加入限制,保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
  2. 可以在特定用途下,可以避免资源重复占用
  3. 比如windows的任务管理器,你开了一个窗口,你再去打开新窗口,他还是只有一个窗口,系统中就只创建和 维护一个窗口
    在这里插入图片描述

懒汉模式

  1. 不是main开始之前就创建,而是的等用到了,才去创建
  2. 把构造函数私有化,这样就不能在外部是实列化对象,这时候,使用静态指针去指向这个类的对象,在类外一开始初始化为NULL,然后设计一个静态成员函数,在这个静态成员函数中实例化一个对象,并返回对象指针。
  3. 懒汉模式,如果多线程中每个线程都会去创建,就有可能创建出多个对象,是线程不安全的的。
    在这里插入图片描述
    程序结束时自动析构的改进:
//代码实例(线程安全) 
template<typename T> 
class Singleton { 
public: 
	static T& getInstance() {
		if (!value_)  value_ = new T(); 
		return *value_; 
    } 
private:      
	class CGarbo{
		public:
			~CGarbo(){
				if(Singleton::value_) delete Singleton::value_;
			} 
	};         

	Singleton();
	~Singleton();
	static T* value_; 
	static CGarbo Garbo;
}; 
template<typename T> T* Singleton<T>::value_ = NULL;

线程安全的改进,通过判断指针以及加锁的方式,两次判断指针是否为空。

class Singleton {
public:
    static Singleton* instance();
    void show();
private:
    static Singleton* singleton;
    static int num;
};

Singleton* Singleton::singleton = nullptr;
int Singleton::num = 0;
mutex Singleton::m;
 
Singleton* Singleton::instance(){
    if(singleton == nullptr){
        m.lock();
        if(singleton == nullptr) {
            singleton = new Singleton();
            num++;
        }
        m.unlock();
    }
    return singleton;
}
 
void Singleton::show(){
    cout << "num : " << num << endl;
}

饿汉模式

  1. 他会在编译阶段就创建出来,在main之前
  2. 如果这个类占用资源比较多的话,程序一开始他就会开始占用资源,即使还没有用到
  3. 饿汉模式一般在main执行之前就创建了,不在多个线程中创建,所以是线程安全的。在这里插入图片描述

代理模式

  1. 为其他对象提供一种代理,用来控制对这个对象的访问
  2. 比如 系统类,我不是直接可以调用它的成员,而是我再通过一个 代理类,通过这个类来调用系统类函数,这样就可以增加比如验证用户、密码这样的功能,这就可以对系统类做一些限制。

外观模式

  1. 就是将复杂的子类系统抽象到同一个接口进行管理,外界只需通过此接口进行交互,而不必直接与复杂的子系统交互
  2. 比如有个 系统基类,有四个子系统类,我要启动这四个子系统对象的时候,我得挨个去调用,这个时候,我写一个外观类,我直接用这一个外观类去启动这些子系统,这样我们呈现给用户的就是很简洁的接口。
  3. 比如 我们有很多比如电视、音响、灯这些类,而我们呢要实现一个ktv功能,就可以把关灯、开电视、开音响这些东西,都封装到一个统一的饿简洁的接口上,调用这个接口就可以了。
    在这里插入图片描述

适配器模式

  1. 类似于电源适配器那样理解,电源是适配器把220v交流带你转成5v直流电给手机充电
  2. 所以适配器模式,就是把一个类的接口转换成用户希望的另外一种接口,使得原本不兼容而不能一起工作的类能够一起工作。
  3. 比如我后面写的类要配合库里面的类进行使用,可能接口就是不好对上,所以需要写一个适配器类去转换一下。

模板方法模式

  1. 比如某个事情或者算法步骤是一样的,只是在每一步上实现细节有不一样,这个时候,就可以创建一个作这件事的的模板,这个模板就框定了做事方法步骤,只是在每步具体的时候用具体的实现
  2. 比如泡茶,泡咖啡,它们步骤方法基本一样,只是每步实现细节不一样。
    在这里插入图片描述
    在这里插入图片描述

策略模式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
创建的角色类,他可动态加载不同的方法,加载什么方法就使用什么方法
在这里插入图片描述

命令模式(动作模式/事务模式)

  1. 抽象出服务端和客户端,客户端向服务端发送请求,服务端处理请求,客户端发不同的请求,服务端响应不同的请求
  2. 在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

观察者模式

在这里插入图片描述

  1. 应用 于一个对象的变化后,需要引起其他对象的变化的情景
  2. 举例就是,比如红绿灯,红绿灯的状态变化后,需要引起等待的车的状态变化,当车来到红绿灯前面的时候,车需要开始观测这个红绿灯,当车离开这个红绿灯之后,就不用看这个红绿灯了。
  3. 之间建立一套触发机制,能降低目标与观察者之间的耦合关系,缺点是两者之间依然有依赖关系,可能出现循环引用,当观察者过多得时候,更新状态花费时间久。
  4. 一个目标类,他里面存储了他的观察者们,他的状态发生变化后,会更新观察者们得状态。

装饰器模式

在这里插入图片描述

  1. 就是不改变原来类的情况下,动态得给这个类增加一些新功能,之前是通过继承实现
  2. 举例比如,有个英雄类,还有很多个装备类,英雄一开始没有装备的,他没什么技能,他可以通过动态的增加一些装备,逐渐有一些技能,逐渐拥有那些装备类的功能。
  3. 还可以通过不同装饰器组合实现更多功能
  4. 缺点是会增加很多子类,程序变复杂
#include <string>
#include <iostream>
//基础组件接口定义了可以被装饰器修改的操作
class Component {
 public:
  virtual ~Component() {}
  virtual std::string Operation() const = 0;
};
//具体组件提供了操作的默认实现。这些类在程序中可能会有几个变体
class ConcreteComponent : public Component {
 public:
  std::string Operation() const override {
    return "ConcreteComponent";
  }
};
//装饰器基类和其他组件遵循相同的接口。这个类的主要目的是为所有的具体装饰器定义封装接口。
//封装的默认实现代码中可能会包含一个保存被封装组件的成员变量,并且负责对齐进行初始化
class Decorator : public Component {
 protected:
  Component* component_;
 public:
  Decorator(Component* component) : component_(component) {
  }
  //装饰器会将所有的工作分派给被封装的组件
  std::string Operation() const override {
    return this->component_->Operation();
  }
};
//具体装饰器必须在被封装对象上调用方法,不过也可以自行在结果中添加一些内容。
class ConcreteDecoratorA : public Decorator {
 //装饰器可以调用父类的是实现,来替代直接调用组件方法。
 public:
  ConcreteDecoratorA(Component* component) : Decorator(component) {
  }
  std::string Operation() const override {
    return "ConcreteDecoratorA(" + Decorator::Operation() + ")";
  }
};
//装饰器可以在调用封装的组件对象的方法前后执行自己的方法
class ConcreteDecoratorB : public Decorator {
 public:
  ConcreteDecoratorB(Component* component) : Decorator(component) {
  }
  std::string Operation() const override {
    return "ConcreteDecoratorB(" + Decorator::Operation() + ")";
  }
};
//客户端代码可以使用组件接口来操作所有的具体对象。这种方式可以使客户端和具体的实现类脱耦
void ClientCode(Component* component) {
  // ...
  std::cout << "RESULT: " << component->Operation();
  // ...
}
int main() {
  Component* simple = new ConcreteComponent;
  std::cout << "Client: I've got a simple component:\n";
  ClientCode(simple);
  std::cout << "\n\n";
  
  Component* decorator1 = new ConcreteDecoratorA(simple);
  Component* decorator2 = new ConcreteDecoratorB(decorator1);
  std::cout << "Client: Now I've got a decorated component:\n";
  ClientCode(decorator2);
  std::cout << "\n";
 
  delete simple;
  delete decorator1;
  delete decorator2;
 
  return 0;
}

版权声明:本文为qq_42657656原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。