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; //禁止赋值
// 静态指针,初始值为 nullptr
static 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(避免继承带来的强耦合)