0 前言
Hello,大家好,欢迎来到『自由技艺』的 C++ 系列专题。代码重用,尽可能避免冗余代码是程序员的一项必备技能,今天就来给大家介绍其中一种:函数装饰器。在设计模式中,与它对应的是类的装饰器模式,但个人觉得很鸡肋,下回再说吧。
1 需求来源
现在假设有一个基础功能函数,但是呢,存在各种应用场景,每种场景都需要对这个函数小修小补、或增加一些定制化的功能。有几种处理方式呢?基类派生?函数重载?建造者模式?都不大合适,而函数装饰器就是一种可参考的手段。
void test(int a, int b) {
std::cout << (a + b) << "n";
}
void do_previous_work() {
std::cout << "do previous works" << "n";
}
void do_after_work() {
std::cout << "do after works" << "n";
}
template<typename RET, typename... ARGS>
std::function<RET(ARGS... args)> decorator(RET(*p_func)(ARGS... args)) {
return [&](ARGS... args) -> RET {
do_previous_work();
test(args...);
(*p_func)(args...);
do_after_work();
};
//没有捕获任何变量的Lambda表达式可以转换成与它的调用原型一致的函数指针
}
int main() {
int a = 10;
int b = 20;
f = test;
f(a, b); // 第一次调用 f
std::function<void(int, int)> f;
f = decorator(test);
f(a, b); // 第二次调用 f
return 0;
}
输出结果:
2 总结
上述代码中有一个基本函数 test,如果需要在 test 之前做某些事情(do_previous_work),在 test 之后也做一些事情(do_after_work),那么定义一个装饰器函数,把 test 封装下,再用函数对象接收就可以了。从结果上看,调用了 f 两次,却输出了不同的结果。如果再有其他需求,类似地再实现一个装饰器函数,同样用 f 接收,再调用,就可以了。
留个思考题,如果把装饰器函数写成如下形式:
template<typename RET, typename... ARGS>
std::function<RET(ARGS... args)> decorator(RET(*p_func)(ARGS... args)) {
auto post = [&](ARGS... args) -> RET {
do_previous_work();
test(args...);
(*p_func)(args...);
do_after_work();
};
return post;
}
这种写法有什么问题吗?欢迎留言区讨论~