C++ 设计模式笔记——7. 相关概念
除了设计模式,还有几个编程概念与之相生相随,尤其在Java中被广泛的应用:面向切面编程、控制反转和依赖注入。 下面简单学习一下这几个概念,并在C++/Python中尝试应用。
面向切面编程
面向切面编程(Aspect-Oriented Programming, AOP)是一种编程范式,它旨在提高软件模块化,通过将横切关注点(Cross-cutting Concerns)与业务逻辑分离来简化程序结构。 横切关注点是指那些分散在多个模块中的功能,如日志记录、事务管理、权限控制等,这些功能虽然对多个模块都重要,但与核心业务逻辑无关。
在Java中,Spring框架广泛支持AOP,例如我们可以使用Spring
AOP来添加日志记录的切面,主要代码如下 1
2
3
4
5
6
7
public class LoggingAspect {
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
17from functools import wraps
def log_before(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}...")
return func(*args, **kwargs)
return wrapper
class UserService:
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
// 服务接口
class Service {
public:
virtual void execute() = 0;
virtual ~Service() = default;
};
// 服务实现
class ServiceImpl : public Service {
public:
void execute() override {
std::cout << "Service is executed." << std::endl;
}
};
// 需要依赖的类
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
// 事件接口
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." << std::endl;
}
};
// 主程序
int main() {
// 创建事件对象
std::shared_ptr<ConcreteEvent> event = std::make_shared<ConcreteEvent>();
// 创建监听器对象
EventListener listener;
// 将监听器注册到事件
event->addListener([&listener]() { listener.onEvent(); });
// 触发事件
event->notify();
return 0;
}