设计模式

设计模式(Design Patterns)是软件开发人员在软件设计过程中面临的一般问题的解决方案。它们不是直接用来完成工作的代码或“完成品”,而是描述如何组织代码和对象,以及它们之间的交互和职责分配的一种抽象方式。设计模式使得代码更加灵活、可重用、易于理解和维护。

设计模式的六大原则
  1. 单一职责原则(SRP):一个类负责一项职责。
  2. 开闭原则(OCP):软件应对扩展开放,对修改关闭。
  3. 里氏替换原则(LSP):子类可以替换父类且功能不受影响。
  4. 依赖倒置原则(DIP):高层模块不应依赖低层模块,而应依赖抽象。
  5. 接口隔离原则(ISP):使用多个小接口比单一大接口好。
  6. 迪米特法则(LoD):一个类应尽量少地了解其他类。

单例模式

单例模式(Singleton Pattern)是一种常用的软件设计模式,用于确保一个类仅有一个实例,并提供一个全局访问点来获取这个唯一实例。这种模式在需要控制资源访问,如数据库连接、文件句柄或者需要确保配置信息的全局唯一性等场景下非常有用。

在C++中,单例模式(Singleton Pattern)是一种确保一个类仅有一个实例,并提供一个全局访问点来获取该实例的设计模式。以下是C++中实现单例模式的几种方式:

饿汉式(Eager Initialization)

饿汉式单例模式在类加载时就完成了实例的初始化,因此它是线程安全的,但可能会导致资源浪费,因为实例在程序启动时就已经创建,即使它可能从未被使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Singleton {
public:
static Singleton* GetInstance() {
return &_slt; // 返回静态成员变量的地址
}

// 其他成员函数...

private:
Singleton() {} // 构造函数私有化
static Singleton _slt; // 静态成员变量,类加载时初始化

// 禁用拷贝构造和赋值构造
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};

// 类外初始化静态成员变量
Singleton Singleton::_slt;
懒汉式(Lazy Initialization,线程不安全)

懒汉式单例模式在需要时才创建实例,但如果不加锁,它在多线程环境下可能会创建多个实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Singleton {
public:
static Singleton* GetInstance() {
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}

// 其他成员函数...

private:
Singleton() {} // 构造函数私有化
static Singleton* instance; // 静态指针成员变量

// 禁用拷贝构造和赋值构造
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};

Singleton* Singleton::instance = nullptr;

注意:上述代码在多线程环境下是不安全的。

懒汉式(Lazy Initialization,线程安全)

为了在多线程环境下保证懒汉式单例模式的线程安全,可以在GetInstance()方法中添加锁。但这种方式可能会影响性能,因为每次调用GetInstance()都需要加锁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <mutex>

class Singleton {
public:
static Singleton* GetInstance() {
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}

// 其他成员函数...

private:
Singleton() {} // 构造函数私有化
static Singleton* instance; // 静态指针成员变量
static std::mutex mtx; // 静态互斥锁

// 禁用拷贝构造和赋值构造
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};

Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;
局部静态变量(C++11及更高版本)

C++11引入了局部静态变量的线程安全初始化,这使得实现单例模式变得更加简单和高效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Singleton {
public:
static Singleton& GetInstance() {
static Singleton instance; // 局部静态变量,线程安全地初始化
return instance;
}

// 其他成员函数...

private:
Singleton() {} // 构造函数私有化

// 禁用拷贝构造和赋值构造
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};

这种方法利用了C++11中的“Magic Static”特性,即在多线程环境下,局部静态变量的初始化是线程安全的。因此,这种方法既简单又高效,是推荐使用的实现方式。

工厂模式

工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的最佳方式,而无需在代码中显式指定所要创建的具体类。工厂模式主要用于创建具有共同接口的对象,但又不希望客户端依赖于这些对象的具体类。工厂模式可以细分为简单工厂模式、工厂方法模式和抽象工厂模式。

简单工厂模式(Simple Factory Pattern)

简单工厂模式又称为静态工厂方法模式,它由一个工厂类根据传入的参数决定创建哪一种类的实例。这种模式通常包括三个角色:工厂类角色、抽象产品角色和具体产品角色。

实现示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>  
#include <string>
#include <memory>

// 抽象产品角色
class Product {
public:
virtual void use() = 0; // 抽象方法
virtual ~Product() {}
};

// 具体产品角色A
class ConcreteProductA : public Product {
public:
void use() override {
std::cout << "Using ConcreteProductA" << std::endl;
}
};

// 具体产品角色B
class ConcreteProductB : public Product {
public:
void use() override {
std::cout << "Using ConcreteProductB" << std::endl;
}
};

// 工厂类角色
class ProductFactory {
public:
static std::unique_ptr<Product> createProduct(const std::string& type) {
if (type == "A") {
return std::make_unique<ConcreteProductA>();
} else if (type == "B") {
return std::make_unique<ConcreteProductB>();
}
return nullptr; // 或者抛出异常
}
};

int main() {
auto productA = ProductFactory::createProduct("A");
if (productA) {
productA->use();
}

auto productB = ProductFactory::createProduct("B");
if (productB) {
productB->use();
}

return 0;
}
工厂方法模式(Factory Method Pattern)

工厂方法模式定义一个用于创建对象的接口,但让子类决定要实例化的类是哪一个。工厂方法让类的实例化推迟到子类中进行。

实现示例(基于上面的示例稍作修改):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 抽象工厂类  
class ProductFactory {
public:
virtual std::unique_ptr<Product> createProduct() = 0; // 工厂方法
virtual ~ProductFactory() {}
};

// 具体工厂类A
class ConcreteFactoryA : public ProductFactory {
public:
std::unique_ptr<Product> createProduct() override {
return std::make_unique<ConcreteProductA>();
}
};

// 具体工厂类B
class ConcreteFactoryB : public ProductFactory {
public:
std::unique_ptr<Product> createProduct() override {
return std::make_unique<ConcreteProductB>();
}
};

// 使用
int main() {
std::unique_ptr<ProductFactory> factoryA = std::make_unique<ConcreteFactoryA>();
auto productA = factoryA->createProduct();
productA->use();

std::unique_ptr<ProductFactory> factoryB = std::make_unique<ConcreteFactoryB>();
auto productB = factoryB->createProduct();
productB->use();

return 0;
}
抽象工厂模式(Abstract Factory Pattern)

抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。它允许客户端使用抽象的接口来创建一组相关的产品,而不需要知道(或关心)实际产出的具体产品是什么。由于它涉及到多个产品族,因此结构相对复杂。

假设我们有两个产品族:一个是汽车制造(包括跑车和SUV),另一个是手机制造(包括智能手机和平板电脑)。每个产品族内部的产品都是相互关联的,并且每个产品族都有其独特的实现方式。

首先,我们定义抽象产品和抽象工厂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 抽象产品
class Vehicle {
public:
virtual void display() = 0;
virtual ~Vehicle() {}
};

class Phone {
public:
virtual void call() = 0;
virtual ~Phone() {}
};

// 抽象工厂
class ProductFactory {
public:
virtual Vehicle* createVehicle() = 0;
virtual Phone* createPhone() = 0;
virtual ~ProductFactory() {}
};

接下来,我们为汽车制造和手机制造分别定义具体的产品和工厂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// 汽车制造的具体产品
class SportsCar : public Vehicle {
public:
void display() override {
std::cout << "Producing a Sports Car\n";
}
};

class SUV : public Vehicle {
public:
void display() override {
std::cout << "Producing an SUV\n";
}
};

// 手机制造的具体产品
class SmartPhone : public Phone {
public:
void call() override {
std::cout << "Calling with a Smart Phone\n";
}
};

class Tablet : public Phone {
public:
void call() override {
std::cout << "Calling (or video calling) with a Tablet\n";
}
};

// 汽车制造的具体工厂
class CarFactory : public ProductFactory {
public:
Vehicle* createVehicle() override {
return new SUV(); // 或者返回 new SportsCar();,取决于你想生产哪种汽车
}

Phone* createPhone() override {
// 汽车工厂不生产手机,所以返回一个null或者抛出一个异常
return nullptr;
}
};

// 手机制造的具体工厂
class PhoneFactory : public ProductFactory {
public:
Vehicle* createVehicle() override {
// 手机工厂不生产汽车,所以返回一个null或者抛出一个异常
return nullptr;
}

Phone* createPhone() override {
return new SmartPhone(); // 或者返回 new Tablet();,取决于你想生产哪种手机
}
};

请注意,在这个例子中,CarFactoryPhoneFactory 实际上并不是完整的抽象工厂实现,因为它们只生产各自领域内的产品。在更复杂的场景中,你可能会希望有一个能够同时生产汽车和手机的工厂(尽管这在现实中可能不太常见)。但为了演示目的,我们保持这两个工厂是独立的。

现在,让我们看看如何使用这些工厂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main() {
ProductFactory* carFactory = new CarFactory();
Vehicle* myCar = carFactory->createVehicle();
myCar->display(); // Producing an SUV

ProductFactory* phoneFactory = new PhoneFactory();
Phone* myPhone = phoneFactory->createPhone();
myPhone->call(); // Calling with a Smart Phone

delete myCar;
delete myPhone;
delete carFactory;
delete phoneFactory;

return 0;
}

这个例子展示了如何在C++中使用抽象工厂模式来创建一系列相关的对象。每个工厂负责生产其特定领域内的产品。客户端代码通过工厂接口与工厂交互,而不需要知道具体的产品实现细节。

观察者模式

观察者模式(Observer Pattern)是一种行为设计模式,用于建立一种对象之间的一对多依赖关系,使得每当一个对象状态发生改变时,其所有依赖者都会得到通知并自动更新。这种模式提供了一种对象设计,让主题(subject)和观察者(observer)之间松耦合,这样它们就可以交互而不需要知道彼此的具体实现细节。

观察者模式的主要角色:
  1. Subject(主题)

    • 抽象主题:定义一个接口,可以增加、删除和通知观察者。
    • 具体主题:存储着状态,当状态改变时,向所有观察者发出通知。
  2. Observer(观察者)

    • 抽象观察者:定义了一个更新接口,使得自身在得到主题的通知时更新自己。
    • 具体观察者:实现抽象观察者的更新接口,以便在得到通知时执行具体操作。
实现示例(C++):

下面是一个简单的C++实现示例,其中有一个Subject类和一个Observer接口,以及它们的具体实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <vector>
#include <string>

// Observer 接口
class Observer {
public:
virtual void update(const std::string& message) = 0;
virtual ~Observer() {}
};

// 具体观察者
class ConcreteObserver : public Observer {
private:
std::string name;

public:
ConcreteObserver(const std::string& name) : name(name) {}

void update(const std::string& message) override {
std::cout << name << " received: " << message << std::endl;
}
};

// Subject 类
class Subject {
private:
std::vector<Observer*> observers;
std::string message;

public:
void attach(Observer* observer) {
observers.push_back(observer);
}

void detach(Observer* observer) {
auto it = std::find(observers.begin(), observers.end(), observer);
if (it != observers.end()) {
observers.erase(it);
}
}

void notify() {
for (auto observer : observers) {
observer->update(message);
}
}

void setMessage(const std::string& message) {
this->message = message;
notify();
}
};

// 使用示例
int main() {
Subject subject;

ConcreteObserver obs1("Observer 1");
ConcreteObserver obs2("Observer 2");

subject.attach(&obs1);
subject.attach(&obs2);

subject.setMessage("Hello, Observers!");

subject.detach(&obs1);

subject.setMessage("Hello again, remaining Observers!");

return 0;
}
关键点:
  • 松耦合:主题和观察者之间通过抽象层进行交互,这使得它们可以独立地改变和复用。
  • 广播通信:当主题状态变化时,所有注册的观察者都会收到通知。
  • 动态关联:观察者可以在运行时被添加或删除,从而允许系统更灵活地改变行为。