随着软件工程的不断发展,除了经典的二十几种设计模式,还有更多设计模式在不断涌现,其中一些设计模式也是值得学习的。

对象池模式

对象池(Object Pool)是一种创建和管理对象的设计模式,特别适用于需要频繁创建和销毁对象的场景。 它通过复用对象来减少对象的创建和销毁次数,从而提高性能和资源利用率,在资源密集型的应用情景中(如数据库连接、线程、Socket连接、内存分配等),池化的思想被广泛使用。

对象池的实现示例是非常简单直观的,直接看代码即可。

与之不同的是,使用C++实现一个实用的线程池或内存池的代码细节会更加复杂,本文中不作讨论。

示例代码如下

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
#include <iostream>
#include <memory>
#include <vector>

// 对象类
class PooledObject {
public:
PooledObject() { std::cout << "PooledObject created.\n"; }

~PooledObject() { std::cout << "PooledObject destroyed.\n"; }

void reset() { std::cout << "PooledObject reset.\n"; }

void doSomething() {
std::cout << "PooledObject is doing something.\n";
}
};

// 对象池类
class ObjectPool {
private:
std::vector<std::shared_ptr<PooledObject>> m_pool;

public:
// 获取对象
std::shared_ptr<PooledObject> getObject() {
if (m_pool.empty()) { return std::make_shared<PooledObject>(); }

std::shared_ptr<PooledObject> obj = m_pool.back();
m_pool.pop_back();
obj->reset();
return obj;
}

// 归还对象
void returnObject(std::shared_ptr<PooledObject> obj) {
m_pool.push_back(obj);
}
};

int main() {
ObjectPool pool;

auto obj1 = pool.getObject();
obj1->doSomething();
pool.returnObject(obj1);

auto obj2 = pool.getObject();
obj2->doSomething();

return 0;
}

运行结果如下

1
2
3
4
5
PooledObject created.
PooledObject is doing something.
PooledObject reset.
PooledObject is doing something.
PooledObject destroyed.

订阅发布模式

订阅发布模式是一种消息传递模式,允许发送者(发布者)和接收者(订阅者)彼此解耦。 在逻辑上主要包括三部分:

  • 发布者(Publisher):发布消息的对象。
  • 订阅者(Subscriber):接收消息的对象。
  • 事件总线(Event Bus):管理发布者和订阅者之间的通信桥梁。

发布者将特定主题的消息发布到一个事件总线上,而订阅者通过事件总线订阅一个或多个主题。 当有新的消息发布时,所有订阅了该主题的订阅者都会收到通知。

示例代码如下

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

// 订阅者基类
class Subscriber {
public:
virtual ~Subscriber() = default;
virtual void onMessage(const std::string &message) = 0;
};

// 事件总线类
class EventBus {
public:
void addsubscriber(const std::string &topic,
std::shared_ptr<Subscriber> subscriber) {
m_subscribers[topic].push_back(subscriber);
}

void publish(const std::string &topic, const std::string &message) {
if (m_subscribers.find(topic) != m_subscribers.end()) {
for (auto &subscriber : m_subscribers[topic]) {
subscriber->onMessage(message);
}
}
}

private:
std::unordered_map<std::string, std::vector<std::shared_ptr<Subscriber>>>
m_subscribers;
};

// 发布者类
class Publisher {
public:
explicit Publisher(EventBus &eventBus) : m_eventBus(eventBus) {}

void publishMessage(const std::string &topic, const std::string &message) {
m_eventBus.publish(topic, message);
}

private:
EventBus &m_eventBus;
};

// 订阅者类
class NewsSubscriber : public Subscriber {
public:
explicit NewsSubscriber(std::string name) : m_name(std::move(name)) {}

void onMessage(const std::string &message) override {
std::cout << m_name << " received news: " << message << '\n';
}

private:
std::string m_name;
};

class SportsSubscriber : public Subscriber {
public:
explicit SportsSubscriber(std::string name) : m_name(std::move(name)) {}

void onMessage(const std::string &message) override {
std::cout << m_name << " received sports news: " << message
<< '\n';
}

private:
std::string m_name;
};

int main() {
EventBus eventBus;

// 创建不同的订阅者
auto newsSubscriber1 =
std::make_shared<NewsSubscriber>("News Subscriber 1");
auto newsSubscriber2 =
std::make_shared<NewsSubscriber>("News Subscriber 2");
auto sportsSubscriber1 =
std::make_shared<SportsSubscriber>("Sports Subscriber 1");

// 添加订阅者以订阅不同主题的消息
eventBus.addsubscriber("news", newsSubscriber1);
eventBus.addsubscriber("news", newsSubscriber2);
eventBus.addsubscriber("sports", sportsSubscriber1);

// 创建发布者
Publisher newsPublisher(eventBus);
Publisher sportsPublisher(eventBus);

// 发布者发布消息到不同主题的消息
newsPublisher.publishMessage("news", "Breaking news: C++20 released!");
sportsPublisher.publishMessage("sports",
"New world record set in marathon!");

return 0;
}

运行结果如下

1
2
3
News Subscriber 1 received news: Breaking news: C++20 released!
News Subscriber 2 received news: Breaking news: C++20 released!
Sports Subscriber 1 received sports news: New world record set in marathon!

订阅发布模式与观察者模式经常被用来对比,两者有很多相似之处,但是也有如下的不同点:

  • 解耦程度:
    • 观察者模式:观察者和被观察者之间存在直接的关联。被观察者需要维护着观察者的列表,并在状态变化时通知这些观察者。
    • 订阅发布模式:发布者和订阅者完全解耦,彼此不知道对方的存在。
  • 通信机制:
    • 观察者模式:被观察者直接调用观察者的更新方法,通知它们状态变化。
    • 订阅发布模式:所有的通信都基于事件总线进行,消息通过事件总线分发给订阅者。
  • 使用场景:
    • 观察者模式:适用于一个对象的状态变化需要通知多个依赖对象的场景,适合较简单的同步通信场景。
    • 订阅发布模式:适用于需要解耦多个组件之间通信的场景,如事件驱动系统、消息队列系统等,适合更复杂的同步或异步通信场景。

事实上,我们可以将订阅发布模式视作广义的观察者模式,订阅发布模式还明显的吸收了中介模式中的部分做法。

补充

除了设计模式,还有几个编程概念与之相生相随,尤其在Java中被广泛的应用:面向切面编程、控制反转和依赖注入。 下面简单学习一下这几个概念,并在C++/Python中尝试应用。

面向切面编程

面向切面编程(Aspect-Oriented Programming, AOP)是一种编程范式,它旨在提高软件模块化,通过将横切关注点(Cross-cutting Concerns)与业务逻辑分离来简化程序结构。 横切关注点是指那些分散在多个模块中的功能,如日志记录、事务管理、权限控制等,这些功能虽然对多个模块都重要,但与核心业务逻辑无关。

在Java中,Spring框架广泛支持AOP,例如我们可以使用Spring AOP来添加日志记录的切面,主要代码如下

1
2
3
4
5
6
7
@Aspect
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Executing: " + joinPoint.getSignature());
}
}

这里@Before注解定义了一个前置通知,它会在匹配的方法调用之前自动执行。 execution(* com.example.service.*.*(..))是一个切点表达式,表示匹配所有在com.example.service包下的方法。

除了上面的定义部分,在Spring框架下还需要启用AOP并配置这个切面,最终达到效果:当服务层的任一方法被调用时,都会先执行日志记录逻辑,将调用信息输出到控制台。

Python虽然没有像Spring AOP这样的框架,但可以使用装饰器来实现类似的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from functools import wraps

def log_before(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}...")
return func(*args, **kwargs)
return wrapper

class UserService:
@log_before
def add_user(self, name):
print(f"Adding user: {name}")

# 使用
service = UserService()
service.add_user("Alice")

C++的语法和标准库并不直接支持AOP,但可以通过元编程、模板或者定制编译器(如AspectC++)来实现AOP的概念:

  • 直接使用C++的lambda表达式、模板元编程等可以实现Python的装饰器,以此实现AOP类似的功能;
  • AspectC++是一个扩展的C++编译器,它通过扩展语法支持直接编写面向切面的代码,扩展语法类似Java的注解。

控制反转与依赖注入

控制反转(Inversion of Control, IoC)是一种编程思想,它将应用程序的控制权从应用代码转移到外部容器或框架。 在传统的编程模式中,对象负责创建或查找它所依赖的对象。 采用控制反转后,这种控制权被“反转”,即对象的创建、生命周期管理以及依赖关系的解决不再由对象本身负责,而是由外部环境(如框架)来控制。

控制反转的一种具体做法是依赖注入(Dependency Injection, DI), 它通过将依赖对象(即组件或服务)注入到需要它们的对象中,从而实现了控制反转。依赖注入可以通过构造函数注入、属性注入或方法注入来实现。

依赖注入的C++示例代码如下

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
#include <iostream>
#include <memory>

// 服务接口
class Service {
public:
virtual void execute() = 0;
virtual ~Service() = default;
};

// 服务实现
class ServiceImpl : public Service {
public:
void execute() override {
std::cout << "Service is executed.\n";
}
};

// 需要依赖的类
class Client {
private:
std::shared_ptr<Service> m_service;

public:
// 构造函数注入
explicit Client(std::shared_ptr<Service> service) : m_service(service) {}

void doSomething() { m_service->execute(); }
};

// 主程序
int main() {
// 创建服务对象
std::shared_ptr<Service> service = std::make_shared<ServiceImpl>();

// 将服务对象注入到Client
Client client(service);

// 使用Client
client.doSomething();

return 0;
}

控制反转并不等于依赖注入,还有其他形式的控制反转,如事件驱动编程中的控制反转,C++示例代码如下

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
#include <functional>
#include <iostream>
#include <memory>
#include <vector>

// 事件接口
class Event {
public:
virtual void notify() = 0;
virtual ~Event() = default;
};

// 事件实现
class ConcreteEvent : public Event {
private:
std::vector<std::function<void()>> m_listeners;

public:
void addListener(std::function<void()> listener) {
m_listeners.push_back(listener);
}

void notify() override {
for (const auto &listener : m_listeners) { listener(); }
}
};

// 事件监听器
class EventListener {
public:
void onEvent() {
std::cout << "EventListener received the event.\n";
}
};

// 主程序
int main() {
// 创建事件对象
std::shared_ptr<ConcreteEvent> event = std::make_shared<ConcreteEvent>();

// 创建监听器对象
EventListener listener;

// 将监听器注册到事件
event->addListener([&listener]() { listener.onEvent(); });

// 触发事件
event->notify();

return 0;
}