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;}
这种写法有什么问题吗?欢迎留言区讨论~
联系客服