首页 > 编程语言 >C++ 设计模式

C++ 设计模式

时间:2024-03-24 22:23:31浏览次数:32  
标签:对象 适配器 观察者 模式 工厂 C++ 设计模式 public

C++ 设计模式

工厂模式:

我们需要方便的使用这些类,减少耦合度

#include <iostream>
#include <string>
using namespace std;
class car{
  public:
  car(string name):name_(name){}
virtual void show();
  protected:
  string name_;
};
class aodi:public car
{
public:
	aodi(string name):car(name){}
    void show(){
    cout<<"this is aodi car!"<<name_<<endl;
    }
};
class baoma:public car{
  public:
baoma(string name):car(name){}
    void show(){
    cout<<"this is baoma car!"<<name_<<endl;
    }
};

简单工厂

简单工厂模式的优缺点

优点

  • 代码简洁: 只需一个工厂类即可创建所有产品对象,代码结构清晰易懂。
  • 降低耦合: 客户端无需了解具体产品的创建细节,只需通过工厂类即可获取所需产品对象,降低了客户端与产品的耦合度。
  • 方便扩展: 只需修改工厂类即可添加新产品,无需修改客户端代码,提高了系统的扩展性。

缺点

  • 工厂类职责过重: 工厂类集中了所有产品的创建逻辑,一旦工厂类出现问题,将导致所有产品无法创建。
  • 违背开闭原则: 添加新产品需要修改工厂类,违背了开闭原则。
  • 灵活性差: 工厂类创建产品对象的逻辑是固定的,无法根据需求动态调整。

适用场景

  • 产品种类较少,且需求相对稳定,无需频繁添加新产品。
  • 产品之间具有共同的父类或接口,可以抽象出创建产品的通用逻辑。
  • 需要降低客户端与产品的耦合度,提高代码的简洁性。

我们用代码例子来理解

enum car_type{
AODI,BAOMA
};
//简单工厂
//创建一个枚举,根据枚举来返回类
class Factor{
  public:
   car * create_car(car_type ctype) const
  {
  if(ctype == AODI)
  {
  return new aodi("aodi");
  }
  else if(ctype == BAOMA)
  {
    return new baoma("baoma");
  }
   else return nullptr;
  }
};


int main()
{
    Factor f;
    car * myCar = f.create_car(AODI);
  myCar->show();
}

工厂方法

工厂方法的优缺点

工厂方法模式是一种创建型设计模式,它定义了一个创建对象的接口,让子类决定创建哪种对象的具体实现。

优点:

  • 封装性: 工厂方法模式将对象的创建逻辑封装在工厂类中,客户端无需了解具体对象的创建细节,提高了代码的封装性。
  • 灵活性: 工厂方法模式可以根据需求动态创建不同的对象,提高了代码的灵活性。
  • 扩展性: 工厂方法模式可以很容易地扩展新的产品类型,符合开闭原则。

缺点:

  • 代码量增加: 工厂方法模式需要为每个产品类型创建一个工厂类,会增加代码量。
  • 抽象层次增加: 工厂方法模式引入了一个抽象的工厂类,增加了代码的复杂度。

适用场景:

  • 产品种类较多,且需求相对稳定,无需频繁添加新产品。
  • 产品之间具有共同的父类或接口,可以抽象出创建产品的通用逻辑。
  • 需要降低客户端与产品的耦合度,提高代码的灵活性。

与简单工厂模式的比较:

  • 简单工厂模式将所有产品的创建逻辑集中在一个工厂类中,代码结构简单,但灵活性差,违背开闭原则。
  • 工厂方法模式将创建产品的逻辑分散到多个工厂类中,提高了灵活性,符合开闭原则,但也增加了代码量。
class Factor{
public:
  virtual car* createCar(string)=0;
};

class aodiFactor:public Factor{
public:
  car* createCar(string name){
  return new aodi(name);
  }
};
 
class baomaFactor:public Factor{
public:
  car* createCar(string name){
  return new baoma(name);
  }
};

抽象工厂

抽象工厂的优缺点

抽象工厂模式是一种创建型设计模式,它提供了一个创建相关产品对象的接口,让子类决定创建哪种具体产品族的对象。

优点:

  • 封装性: 抽象工厂模式将创建产品的逻辑封装在工厂类中,客户端无需了解具体产品的创建细节,提高了代码的封装性。
  • 灵活性: 抽象工厂模式可以根据需求动态创建不同产品族的对象,提高了代码的灵活性。
  • 扩展性: 抽象工厂模式可以很容易地扩展新的产品族,符合开闭原则。
  • 强制一致性: 抽象工厂模式可以确保客户端只能使用同一个产品族中的对象,提高了代码的一致性。

缺点:

  • 代码量增加: 抽象工厂模式需要为每个产品族创建一个工厂类,会增加代码量。
  • 抽象层次增加: 抽象工厂模式引入了一个抽象的工厂类和一系列抽象产品类,增加了代码的复杂度。

适用场景:

  • 需要创建一系列相关的对象,并且这些对象之间存在一定的约束关系的场景。
  • 产品种类较多,且需求相对稳定,无需频繁添加新产品。
  • 需要降低客户端与产品的耦合度,提高代码的灵活性。

与工厂方法模式的比较:

  • 工厂方法模式只针对单个产品类型,而抽象工厂模式可以针对多个产品类型。
  • 抽象工厂模式可以强制一致性,确保客户端只能使用同一个产品族中的对象。
#include <iostream>
#include <string>
#include <memory>
using namespace std;
// 汽车类
class car{
  public:
    car(string name):name_(name){}
    virtual void show()=0;
  protected:
  string name_;
};

// 车灯类
class light{
public:
virtual void show()=0;
};




// 奥迪汽车
class aodi:public car
{
public:
	aodi(string name):car(name){}
    void show() override {
    cout<<"this is aodi car!"<<name_<<endl;
    }
};
// 奥迪车灯
class aodilight:public light{
  public:
  void show() override{
    cout<<"ao di show() light"<<endl;
  }

};


// 宝马汽车
class baoma:public car{
  public:
baoma(string name):car(name){}
    void show() override {
    cout<<"this is baoma car!"<<name_<<endl;
    }
};
// 宝马车灯
class baomalight:public light{
  void show() override{
    cout<<"bao ma show() light"<<endl;
  }
};


// 抽象工厂模式
class Factor{
public:
  virtual car* createCar(string)=0;
  virtual light* createLight()=0;
};

// 奥迪工厂
class aodiFactor:public Factor{
public:
    car* createCar(string name)override{
      return new aodi(name);
    }
    light* createLight()override{
      return new aodilight();
    }
};


// 宝马工厂
class baomaFactor:public Factor{
public:
    car* createCar(string name)override{
      return new baoma(name);
    }
    light* createLight()override{
      return new baomalight();
    }
};

int main()
{
unique_ptr<Factor> af(new aodiFactor());
unique_ptr<car> ac(af->createCar("aodi"));
unique_ptr<light> al(af->createLight());
ac->show();
al->show();

}

单例模式

懒汉单例模式:

  • 在第一次使用单例对象时才进行实例化
  • 优点:节省内存资源,避免在程序启动时就创建不使用的对象
  • 缺点:线程安全性问题,需要加锁保证线程安全

饿汉单例模式:

  • 在程序启动时就进行实例化
  • 优点:线程安全,无需加锁
  • 缺点:浪费内存资源,无论是否使用都会在程序启动时创建对象

总结:

特性 懒汉单例模式 饿汉单例模式
实例化时间点 首次使用时 程序启动时
线程安全性 需要加锁 无需加锁
内存资源 节省 浪费

饿汉单例模式

// 饿汉单例模式
#ifndef 0
class Cl{
public:

static Cl* getCl(){
  return &cl_;
}

private:
Cl(const Cl&)=delete;
Cl& operator=(const Cl&)=delete;
Cl(){}
static Cl cl_;
};
Cl Cl::cl_;

#endif

线程安全单例模式

懒汉单例模式

// 懒汉单例模式
mutex mutex1;
class Cl{
public:
static Cl* getCl(){
  if(c_ ==nullptr)
  {
    lock_guard<mutex> lg(mutex1);
    c_ = new Cl();
  }
  return c_;
}

private:
Cl()=default;
Cl(const Cl&)=delete;
Cl& operator=(const Cl&)=delete;

static Cl* c_;
};
Cl* Cl::c_=nullptr;

观察者模式

观察者模式是一种设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象(被观察者)。当主题对象的状态发生更改时,会通知所有观察者,让它们能够自动更新。

观察者模式的结构:

  • 主题对象(Subject): 也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
  • 具体主题对象(Concrete Subject): 也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
  • 观察者对象(Observer): 也叫抽象观察者类,它定义了观察者需要实现的更新方法。
  • 具体观察者对象(Concrete Observer): 具体观察者类,实现抽象观察者类中的更新方法,并根据主题对象的改变做出相应的反应。

观察者模式的优点:

  • 降低耦合度:主题对象和观察者对象之间通过接口进行交互,耦合度低,易于维护和扩展。
  • 提高灵活性:观察者可以自由注册和注销,主题对象无需关心观察者的具体实现。
  • 支持多播:主题对象可以同时通知多个观察者,提高效率。

观察者模式的应用场景:

  • 界面更新:当数据发生变化时,自动更新界面。
  • 事件处理:当事件发生时,通知所有相关的观察者进行处理。
  • 状态监控:监控主题对象的内部状态,并根据状态变化进行相应的处理。
#include <iostream>
#include <unordered_map>
class Observer;
// 抽象主题类
class Subject {
public:
  virtual ~Subject() {}
  virtual void Attach(Observer* observer) = 0; // 添加观察者
  virtual void Detach(Observer* observer) = 0; // 删除观察者
  virtual void Notify() = 0; // 通知所有观察者
};

// 抽象观察者类
class Observer {
public:
  virtual ~Observer() {}
  virtual void Update() = 0; // 更新
};

// 具体主题类
class ConcreteSubject : public Subject {
public:
  ConcreteSubject() {}
  ~ConcreteSubject() {}

  void Attach(Observer* observer) override {
    observers_[observer] = true; // 将观察者添加到哈希表中
  }

  void Detach(Observer* observer) override {
    observers_.erase(observer); // 从哈希表中删除观察者
  }

  void Notify() override {
    for (auto it = observers_.begin(); it != observers_.end(); ++it) {
      it->first->Update(); // 通知所有观察者
    }
  }

private:
  std::unordered_map<Observer*, bool> observers_; // 存储所有观察者的哈希表
};

// 具体观察者类
class ConcreteObserver1 : public Observer {
public:
  ConcreteObserver1() {}
  ~ConcreteObserver1() {}

  void Update() override {
    std::cout << "ConcreteObserver1 received notification." << std::endl;
  }
};

// 具体观察者类
class ConcreteObserver2 : public Observer {
public:
  ConcreteObserver2() {}
  ~ConcreteObserver2() {}

  void Update() override {
    std::cout << "ConcreteObserver2 received notification." << std::endl;
  }
};

int main() {
  // 创建主题对象
  ConcreteSubject* subject = new ConcreteSubject();

  // 创建观察者对象
  ConcreteObserver1* observer1 = new ConcreteObserver1();
  ConcreteObserver2* observer2 = new ConcreteObserver2();

  // 将观察者添加到主题中
  subject->Attach(observer1);
  subject->Attach(observer2);

  // 主题状态发生改变
  subject->Notify();

  // 删除观察者
  subject->Detach(observer1);

  // 主题再次发生改变
  subject->Notify();

  delete subject;
  delete observer1;
  delete observer2;

  return 0;
}

代理模式

为什么需要代理模式

1、客户端可能无法直接操作对象(在另一台机器),那么可以在客户端建立一个代理对象,然后客户端直接调用目标对象,代理对象在与目标对象建立联系

2、需要加强目标对象的功能。有人表示为什么不在当初的类中加好,要使用代理来补充。单一职责要求一个类的功能尽可能单一,而且增强的功能大多都是与之前业务不同,比如增加日志输出,权限判断等,本来就不应该由之前的类去承担

#include <iostream>

class Subject {
public:
  virtual ~Subject() {}
  virtual void Request() = 0; // 请求方法
};

class RealSubject : public Subject {
public:
  RealSubject() {}
  ~RealSubject() {}

  void Request() override {
    std::cout << "RealSubject::Request()" << std::endl;
  }
};

class Proxy : public Subject {
public:
  Proxy() {}
  ~Proxy() {}

  void Request() override {
    std::cout << "Proxy::Request()" << std::endl;
    realSubject_->Request(); // 调用真实对象的 Request() 方法
  }

private:
  RealSubject* realSubject_ = new RealSubject(); // 真实对象
};

int main() {
  Subject* subject = new Proxy(); // 创建代理对象
  subject->Request(); // 调用代理对象的 Request() 方法

  delete subject;

  return 0;
}

适配器模式

适配器模式介绍

适配器模式是一种结构型设计模式,用于将一个类的接口转换成客户端所期望的另一种接口。适配器模式允许不兼容的接口之间进行协作。它可以将现有代码与外部库、框架或API集成起来,而无需修改已有代码。

适配器模式的结构如下:

  • 目标接口:定义客户端期望的接口。
  • 适配器类:实现目标接口,并包含对适配者类的引用。
  • 适配者类:定义已有的接口。

适配器模式的工作原理如下:

  1. 客户端想要使用目标接口。
  2. 适配器类实现了目标接口。
  3. 适配器类包含对适配者类的引用。
  4. 适配器类将客户端的请求转发给适配者类。
  5. 适配者类处理请求并返回结果。
  6. 适配器类将结果返回给客户端。

为什么要使用适配器模式?

适配器模式具有以下优点:

  • 提高代码的复用性:适配器模式允许你重复使用现有的类或接口,而不需要修改它们的代码。
  • 提高系统的灵活性:适配器模式可以将不兼容的接口转换为兼容的接口,从而提高了系统的灵活性,使得系统更加容易进行扩展和维护。
  • 降低耦合度:适配器模式可以降低类之间的耦合度,使得代码更加易于理解和维护。

适配器模式的应用场景

适配器模式可以应用于以下场景:

  • 将旧的接口转换为新的接口:当系统的接口发生变化时,可以使用适配器模式将旧的接口转换为新的接口,从而使旧的代码能够继续使用。
  • 将不同的接口统一起来:当系统中存在多个不同的接口时,可以使用适配器模式将这些接口统一起来,从而使客户端能够以统一的方式使用这些接口。
  • 将第三方库或框架集成到系统中:当需要将第三方库或框架集成到系统中时,可以使用适配器模式将第三方库或框架的接口转换为系统期望的接口。

适配器模式的示例

以下是一个使用适配器模式的示例:

#include <iostream>

class Target {
public:
  virtual void Request() = 0;
};

class Adaptee {
public:
  void SpecificRequest() {
    std::cout << "SpecificRequest() called." << std::endl;
  }
};

class Adapter : public Target {
public:
  Adapter(Adaptee* adaptee) : adaptee_(adaptee) {}

  void Request() override {
    adaptee_->SpecificRequest();
  }

private:
  Adaptee* adaptee_;
};

int main() {
  Adaptee* adaptee = new Adaptee();
  Adapter* adapter = new Adapter(adaptee);

  adapter->Request();

  return 0;
}

在这个示例中,Target 类定义了一个目标接口。Adaptee 类定义了一个已有的接口。Adapter 类实现了目标接口,并包含对 Adaptee 类的引用。

main() 函数中,创建了一个 Adaptee 对象和一个 Adapter 对象。Adapter 对象将 Adaptee 对象的 SpecificRequest() 方法包装成 Request() 方法,从而使客户端能够以统一的方式使用 Adaptee 对象。

输出:

SpecificRequest() called.

装饰器模式

装饰器模式是一种结构型设计模式,它允许向一个现有对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

C++ 中的装饰器模式示例

以下是一个 C++ 中的装饰器模式示例:

#include <iostream>

class Shape {
public:
  virtual void draw() = 0;
};

class Circle : public Shape {
public:
  void draw() override {
    std::cout << "Drawing a circle" << std::endl;
  }
};

class Rectangle : public Shape {
public:
  void draw() override {
    std::cout << "Drawing a rectangle" << std::endl;
  }
};

class ColoredShape : public Shape {
protected:
  Shape* shape;
  std::string color;

public:
  ColoredShape(Shape* shape, const std::string& color) : shape(shape), color(color) {}

  void draw() override {
    shape->draw();
    std::cout << " with color " << color << std::endl;
  }
};

int main() {
  Shape* circle = new Circle();
  circle->draw();

  Shape* redCircle = new ColoredShape(circle, "red");
  redCircle->draw();

  Shape* rectangle = new Rectangle();
  rectangle->draw();

  Shape* greenRectangle = new ColoredShape(rectangle, "green");
  greenRectangle->draw();

  return 0;
}

在这个示例中,Shape 是一个抽象类,它定义了一个 draw() 方法。CircleRectangleShape 的子类,它们分别实现了 draw() 方法来绘制圆形和矩形。

ColoredShape 是一个装饰器类,它包装了一个 Shape 对象并添加了颜色功能。ColoredShapedraw() 方法首先调用其包装的 Shape 对象的 draw() 方法,然后输出颜色信息。

在 main 函数中,我们创建了几个形状对象并使用它们来演示装饰器模式。

输出:

Drawing a circle
Drawing a circle with color red
Drawing a rectangle
Drawing a rectangle with color green

在这个示例中,ColoredShape 装饰器类允许我们向 Shape 对象添加颜色功能,而无需修改 Shape 类本身。这使得我们可以灵活地添加新功能到现有对象,而无需改变它们的结构。

标签:对象,适配器,观察者,模式,工厂,C++,设计模式,public
From: https://www.cnblogs.com/AndreaDO/p/18090856

相关文章

  • LeetCode第390场周赛题解(c++)
    真的无语了,早上怎么都提交不了,显示未知错误。。。结果晚上就可以提交了。唉100245.每个字符最多出现两次的最长子字符串给你一个字符串 s ,请找出满足每个字符最多出现两次的最长子字符串,并返回该子字符串的 最大 长度。示例1:输入: s="bcbbbcba"输出: 4解释:以......
  • MVC设计模式的详解及应用
    文章目录一、MVC是什么二、MVC的优点三、MVC的应用领域一、MVC是什么MVC全名是ModelViewController,是一种软件设计典范,也是一个框架模式。它将应用程序分成三个核心部件:模型(Model)、视图(View)和控制器(Controller)。每个部件都有自己的任务和责任。模型(M......
  • c++十大排序——快速排序
    1#include<iostream>usingnamespacestd;voidquickSort(intarr[],intbegin,intend){ if(begin>=end)return; intleft=begin; intright=end; inttemp=arr[left]; while(left<right){ //从后往前找比他小的放前面,从前往后找比它大的放后......
  • 【C++】每日一题 452 用最少数量的箭引爆气球
    有一些球形气球贴在一堵用XY平面表示的墙面上。墙面上的气球记录在整数数组points,其中points[i]=[xstart,xend]表示水平直径在xstart和xend之间的气球。你不知道气球的确切y坐标。一支弓箭可以沿着x轴从不同点完全垂直地射出。在坐标x处射出一支箭,若有......
  • 初识C++(二)引用,内联函数,auto
    目录1.引用的概念与用法:1.1引用特性:1.2使用场景    1.2.1做参数1.3传值、传引用效率比较1.4引用做返回值1.5引用和指针的对比2.内联函数3.auto关键字4.基于范围的for循环(C++11)5.指针空值nullptr(C++11)1.引用的概念与用法:    引用是一个重要的......
  • 代码解析 C++ 的语法难点
    C++是一种强大的编程语言,它提供了丰富的语法特性,但也带来了相应的语法难点。本文将深入解析C++中一些常见的语法难点,并提供清晰的解释和示例,帮助读者深入理解C++的语法。1.指针和引用指针和引用都是C++中处理内存地址的强大工具,但它们也容易混淆。指针指向内存地......
  • 设计模式-单例模式(模拟面试官八股)
    一、请说下设计模式中,你熟悉的设计模式单例模式二、单例模式你有了解吗单例模式是创建型设计模式,该类负责创建自己的对象,他确保一个类只有一个实例,并且提拱了一个去全局访问点来访问该实例。二、单例模式有几种实现方式 1.饿汉式(静态常量)优点:写法容易,在类加载的时候就......
  • JAVAEE——多线程的设计模式,生产消费模型,阻塞队列
    文章目录多线程设计模式什么是设计模式单例模式饿汉模式懒汉模式线程安全问题懒汉模式就一定安全吗?锁引发的效率问题jvm的优化引起的安全问题阻塞队列阻塞队列是什么?生产消费者模型阻塞队列实现消费生产者模型可能遇到的异常多线程设计模式什么是设计模式首先我......
  • 第十二届蓝桥杯省赛C&C++ 研究生组
    十二届省赛题第十二届蓝桥杯省赛C&C++研究生组-卡片第十二届蓝桥杯省赛C&C++研究生组-直线第十二届蓝桥杯省赛C&C++研究生组-货物摆放第十二届蓝桥杯省赛C&C++研究生组-路径第十二届蓝桥杯省赛C&C++研究生组-时间显示第十二届蓝桥杯省赛C&C++研究生组-砝码称重......
  • C++ 的标准模板库(STL)常用容器介绍
    C++的标准模板库(STL)提供了丰富的容器类来帮助开发者管理和存储数据。下面我将介绍C++中常用的STL容器,并且为每个容器提供一个简单的示例来说明其基本用法。1.vector(向量)#include<iostream>#include<vector>intmain(){std::vector<int>vec;//添加元......