1、单例模式
确保一个类只有一个实例,并提供全局访问点。
单例模式又分为饿汉式和懒汉式懒汉式。
饿汉式 --- 线程安全但浪费空间 --- 在程序启动适合就创建好了单例实例
懒汉式 --- 不浪费空间但线程不安全 --- 提供静态函数来实例化单例
编码实现:
// 饿汉式
class SingletonEager{
private::SingletonEager(){} //私有构造函数SingletonEager(const SingletonEager&) = delete; //禁止拷贝构造SingletonEager& operator=(const SingletonEager&) = delete; //禁止赋值public:static SingletonEager instanse; //静态实例static SingletonEager& getInstanse(){return instanse;}void doSomeThing(){}}SingletonEager SingletonEager::instanse; //类外初始化,程序启动适合进行初始化//只能直接通过静态函数调用SingletonEager::getInstanse().doSomeThing();// 懒汉式 (没考虑线程安全,可以使用线程锁避免多个线程同时调用静态函数)
class SingletonLazy {
private::SingletonLazy (){} //私有构造函数SingletonLazy (const SingletonLazy &) = delete; //禁止拷贝构造SingletonLazy & operator=(const SingletonLazy &) = delete; //禁止赋值// 静态指针,初始值为 nullptrstatic SingletonLazy* instance;public:static SingletonLazy * getInstanse(){//局部静态实例,在第一次调用的时候初始化instanse = new SingletonLazy (); return instanse;}void doSomeThing(){}}// 初始化静态成员指针为 nullptr
SingletonLazy* SingletonLazy::instance = nullptr;//只能直接通过静态函数调用
SingletonLazy ::getInstanse()->doSomeThing();
2、普通工厂模式/静态工厂模式
实现步骤:定义产品基类与接口 --- 实现具体产品类 --- 定义工厂类(通常包含一个静态方法,使用多态,根据传入参数判断并创建相应的产品对象)
// 产品抽象类
class Product{
pubulic:virtual void use() = 0; //产品使用方法virtual ~Product(){};
}// 具体产品A
class ProductA{
pubulic:virtual void use() override{ //A产品使用方法cout << "use ProductA" << endk;}
}// 具体产品B
class ProductB{
pubulic:virtual void use() override{ //A产品使用方法cout << "use ProductB" << endk;}
}class SimpleFactory {
public://常常包含一个静态方法static Product* createProduct(const string& type){if(type == 'A'){return new ProductA();}else if(ype == 'B'){return new ProductB();}else return nullptr;}}// 调用
Product* product =SimpleFactory::createProduct("A"); //记得delete product;
3、抽象工厂模式
抽象工厂模式相较于工厂模式是将工厂也抽象化,用于创建一系列相关或者相互依赖的产品族
// 产品接口
class Button {
public:virtual void paint() = 0;virtual ~Button() {}
};class TextBox {
public:virtual void render() = 0;virtual ~TextBox() {}
};// Windows 风格产品
class WindowsButton : public Button {
public:void paint() override { /* Windows 按钮绘制逻辑 */ }
};class WindowsTextBox : public TextBox {
public:void render() override { /* Windows 文本框渲染逻辑 */ }
};// Mac 风格产品
class MacButton : public Button {
public:void paint() override { /* Mac 按钮绘制逻辑 */ }
};class MacTextBox : public TextBox {
public:void render() override { /* Mac 文本框渲染逻辑 */ }
};// 抽象工厂接口
class GUIFactory {
public:virtual Button* createButton() = 0;virtual TextBox* createTextBox() = 0;virtual ~GUIFactory() {}
};// Windows 工厂
class WindowsFactory : public GUIFactory {
public:Button* createButton() override {return new WindowsButton();}TextBox* createTextBox() override {return new WindowsTextBox();}
};// Mac 工厂
class MacFactory : public GUIFactory {
public:Button* createButton() override {return new MacButton();}TextBox* createTextBox() override {return new MacTextBox();}
};void createGUI(GUIFactory* factory) {Button* btn = factory->createButton();TextBox* txt = factory->createTextBox();btn->paint();txt->render();// 注意清理内存...
}//再比如水果族//抽象苹果
//中国苹果
//日本苹果
//美国苹果//抽象香蕉
//中国香蕉
//日本香蕉
//美国香蕉//抽象工厂
//中国工厂(含中国水果)
//日本工厂
//美国工厂void createFruit(const 抽象工厂& factor){苹果* 苹果 = factor->create苹果;香蕉* 香蕉 = factor->create香蕉;
}
4、建造者模式
建造者模式允许你一步一步地构造一个复杂对象,而不必在一个巨大的构造函数中处理所有细节。
实现步骤:产品 --- 抽象建造者 --- 具体建造者 --- 指挥者
// 使用建造者模式构造一个简单的“房子”对象。房子由墙壁、屋顶和地基组成,
// 不同的具体建造者可以构造出不同风格的房子。
#include <iostream>
#include <string>
#include <memory>// 产品类:房子
class House {
public:void setFoundation(const std::string &foundation) {foundation_ = foundation;}void setStructure(const std::string &structure) {structure_ = structure;}void setRoof(const std::string &roof) {roof_ = roof;}void show() const {std::cout << "House built with:\n"<< "Foundation: " << foundation_ << "\n"<< "Structure: " << structure_ << "\n"<< "Roof: " << roof_ << std::endl;}
private:std::string foundation_;std::string structure_;std::string roof_;
};// 抽象建造者
class HouseBuilder {
public:virtual ~HouseBuilder() {}virtual void buildFoundation() = 0;virtual void buildStructure() = 0;virtual void buildRoof() = 0;virtual std::unique_ptr<House> getHouse() = 0;
};// 具体建造者:建造现代风格的房子
class ModernHouseBuilder : public HouseBuilder {
public:ModernHouseBuilder() {house_ = std::make_unique<House>();}void buildFoundation() override {house_->setFoundation("Concrete foundation");}void buildStructure() override {house_->setStructure("Glass and steel structure");}void buildRoof() override {house_->setRoof("Flat roof");}std::unique_ptr<House> getHouse() override {return std::move(house_);}
private:std::unique_ptr<House> house_;
};// 指挥者:负责按照步骤构造房子
class Director {
public:void setBuilder(HouseBuilder *builder) {builder_ = builder;}// 构造房子void constructHouse() {if(builder_) {builder_->buildFoundation();builder_->buildStructure();builder_->buildRoof();}}
private:HouseBuilder* builder_ = nullptr;
};int main() {Director director;ModernHouseBuilder modernBuilder;director.setBuilder(&modernBuilder);director.constructHouse();std::unique_ptr<House> house = modernBuilder.getHouse();house->show();return 0;
}
5、适配器模式(对象适配器)
组成:目标接口(客户希望) 已有接口 适配器(将用户接口转换成已有接口的方法)
假设我们有一个已有类 LegacyPrinter
,它提供了打印功能,但接口与客户端预期的 Printer
接口不一致,我们需要创建一个适配器将 LegacyPrinter
的接口转换为 Printer
接口。
#include <iostream>
#include <string>// 目标接口,客户端期望使用该接口
class Printer {
public:virtual void print(const std::string &text) = 0;virtual ~Printer() {}
};// 已有类,其接口与目标接口不匹配
class LegacyPrinter {
public:void oldPrint(const char* text) {std::cout << "LegacyPrinter prints: " << text << std::endl;}
};// 对象适配器:包装 LegacyPrinter,实现目标接口 Printer
class PrinterAdapter : public Printer {
public:PrinterAdapter(LegacyPrinter* legacy) : legacyPrinter(legacy) {}void print(const std::string &text) override {// 将 std::string 转换为 const char*,调用已有方法legacyPrinter->oldPrint(text.c_str());}private:LegacyPrinter* legacyPrinter;
};int main() {// 创建已有对象LegacyPrinter legacyPrinter;// 用适配器包装已有对象Printer* printer = new PrinterAdapter(&legacyPrinter);// 客户端通过目标接口调用方法,不关心内部实现细节printer->print("Hello, Adapter Pattern!");delete printer;return 0;
}
6、装饰模式
结构型设计模式,用于动态地给对象添加功能,而不改变其接口。通过装饰模式,我们可以在运行时“包装”一个对象,并在包装过程中扩展或修改其行为,从而避免了通过继承来增加功能所带来的类爆炸问题。
组成:组件接口 --- 具体组件 --- 抽象装饰器 --- 具体装饰器
下面给出一个简单的 C++ 示例,展示如何使用装饰模式为一个文本打印器添加额外的装饰功能。
#include <iostream>
#include <string>// 组件接口:定义打印的接口
class Printer {
public:virtual void print(const std::string &text) = 0;virtual ~Printer() {}
};// 具体组件:基本的打印器
class BasicPrinter : public Printer {
public:void print(const std::string &text) override {std::cout << text;}
};// 抽象装饰器:实现 Printer 接口,并持有一个 Printer 对象
class PrinterDecorator : public Printer {
protected:Printer* printer;
public:PrinterDecorator(Printer* p) : printer(p) {}virtual ~PrinterDecorator() {delete printer;}void print(const std::string &text) override {// 委托调用被装饰对象的方法printer->print(text);}
};// 具体装饰器:为打印增加前后边框
class BorderPrinter : public PrinterDecorator {
public:BorderPrinter(Printer* p) : PrinterDecorator(p) {}void print(const std::string &text) override {std::cout << "-----------------" << std::endl;PrinterDecorator::print(text);std::cout << std::endl << "-----------------" << std::endl;}
};// 另一个具体装饰器:为打印增加日志记录功能
class LoggingPrinter : public PrinterDecorator {
public:LoggingPrinter(Printer* p) : PrinterDecorator(p) {}void print(const std::string &text) override {std::cout << "[LOG] About to print text." << std::endl;PrinterDecorator::print(text);std::cout << std::endl << "[LOG] Finished printing." << std::endl;}
};int main() {// 创建一个基本打印器Printer* basic = new BasicPrinter();// 用 BorderPrinter 装饰基本打印器,为文本添加边框Printer* bordered = new BorderPrinter(basic);// 再用 LoggingPrinter 装饰 bordered 打印器,增加日志记录Printer* decoratedPrinter = new LoggingPrinter(bordered);// 使用装饰后的打印器decoratedPrinter->print("Hello, Decorator Pattern!");delete decoratedPrinter;return 0;
}
7、策略模式
一种行为型设计模式,其核心思想是将一系列算法封装成独立的策略类,使它们可以互换使用,从而使得算法的变化独立于使用它的客户端。换句话说,策略模式允许在运行时选择不同的算法或行为,而无需修改使用这些算法的上下文对象。
组成:策略接口 --- 具体策略 --- 上下文
下面给出一个简单的 C++ 示例,展示如何使用策略模式来实现不同的排序算法。假设我们需要对一组数字排序,排序策略可以是冒泡排序、快速排序等,我们将这些排序算法分别封装为策略类。
#include <iostream>
#include <vector>
#include <algorithm>// 策略接口,定义排序算法
class SortStrategy {
public:virtual void sort(std::vector<int>& data) = 0;virtual ~SortStrategy() {}
};// 具体策略:冒泡排序
class BubbleSort : public SortStrategy {
public:void sort(std::vector<int>& data) override {int n = data.size();for (int i = 0; i < n - 1; ++i) {for (int j = 0; j < n - i - 1; ++j) {if (data[j] > data[j + 1]) {std::swap(data[j], data[j + 1]);}}}std::cout << "Data sorted using BubbleSort." << std::endl;}
};// 具体策略:标准库排序(快速排序的一个实现)
class QuickSort : public SortStrategy {
public:void sort(std::vector<int>& data) override {std::sort(data.begin(), data.end());std::cout << "Data sorted using QuickSort (std::sort)." << std::endl;}
};// 上下文类,使用策略接口进行排序
class SortContext {
private:SortStrategy* strategy;
public:SortContext(SortStrategy* strat = nullptr) : strategy(strat) {}// 设置策略void setStrategy(SortStrategy* strat) {strategy = strat;}// 执行排序void executeSort(std::vector<int>& data) {if (strategy) {strategy->sort(data);} else {std::cout << "No sorting strategy set." << std::endl;}}
};int main() {// 创建一个包含无序数据的数组std::vector<int> data = {5, 3, 8, 4, 2, 7, 1, 6};// 创建上下文对象SortContext context;// 使用冒泡排序策略BubbleSort bubble;context.setStrategy(&bubble);context.executeSort(data);// 输出排序结果std::cout << "Sorted Data: ";for (int num : data) {std::cout << num << " ";}std::cout << std::endl;// 打乱数据data = {5, 3, 8, 4, 2, 7, 1, 6};// 使用快速排序策略(std::sort实现)QuickSort quick;context.setStrategy(&quick);context.executeSort(data);// 输出排序结果std::cout << "Sorted Data: ";for (int num : data) {std::cout << num << " ";}std::cout << std::endl;return 0;
}
8、模板方法模式
一种行为型设计模式,其核心思想是定义一个算法的骨架,将一些步骤的具体实现延迟到子类中。这样,父类中就能封装不变的部分,而将变化的部分留给子类实现,从而实现代码复用和算法灵活扩展。
组成:抽象类 --- 具体子类
假设我们有一个数据处理流程,需要从文件中读取数据、处理数据,再将处理结果写入文件。数据处理的整体流程固定,但具体的数据读取和处理方式可能因场景不同而有所变化。
#include <iostream>
#include <string>// 抽象类,定义数据处理模板
class DataProcessor {
public:// 模板方法:定义算法骨架,不能被子类重写void process() {readData();processData();writeData();}virtual ~DataProcessor() {}protected:// 抽象步骤:读取数据virtual void readData() = 0;// 抽象步骤:处理数据virtual void processData() = 0;// 抽象步骤:写入数据virtual void writeData() = 0;
};// 具体子类:处理文本数据
class TextDataProcessor : public DataProcessor {
protected:void readData() override {std::cout << "Reading text data from file..." << std::endl;// 模拟读取数据操作}void processData() override {std::cout << "Processing text data..." << std::endl;// 模拟数据处理操作}void writeData() override {std::cout << "Writing processed text data to file..." << std::endl;// 模拟写入数据操作}
};// 具体子类:处理二进制数据
class BinaryDataProcessor : public DataProcessor {
protected:void readData() override {std::cout << "Reading binary data from file..." << std::endl;// 模拟读取二进制数据操作}void processData() override {std::cout << "Processing binary data..." << std::endl;// 模拟二进制数据处理操作}void writeData() override {std::cout << "Writing processed binary data to file..." << std::endl;// 模拟写入二进制数据操作}
};int main() {// 使用模板方法模式进行文本数据处理DataProcessor* processor1 = new TextDataProcessor();processor1->process(); // 调用模板方法,依次执行读、处理、写delete processor1;std::cout << "------------------------" << std::endl;// 使用模板方法模式进行二进制数据处理DataProcessor* processor2 = new BinaryDataProcessor();processor2->process();delete processor2;return 0;
}
除此之外我们也应该了解软件设计的七大原则,来帮助我们实现高质量的编程
软件设计七大原则(OOP原则)
- 单一职责原则:一个类只负责一项职责(比如一个类负责存储,另一个类负责显示)
- 开闭原则:软件实体(类、模块、函数)应该对扩展开放,对修改关闭。(使用抽象类定义接口行为,通过继承和多态实现扩展,而不是直接修改原有的类)
- 里氏替换原则:子类必须可以替换其父类,且不会影响程序的正确性(确保合理继承,比如父类是鸟,会飞的鸟,则企鹅不能继承会飞的鸟类)
- 接口隔离原则:客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上(将一个大接口拆分成多个小接口)
- 依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖抽象;抽象不应该依赖细节,而是细节依赖抽象
- 迪米特法则:一个对象应该对其他对象有最少的了解,只与直接朋友通信(避免链式调用)
- 合成复用原则:尽量使用组合或者聚合has-a,而不是继承is-a(避免继承带来的强耦合)