原创C++设计模式:功能归一化——无继承、轻量版AOP,比传统OOP更优雅

原创C++设计模式:功能归一化------无继承、轻量版AOP,比传统OOP更优雅

文章目录

在C++面向对象开发中,我们总会遇到一个共性痛点:多个毫无关联的实体类,需要重复编写相同的公共操作(比如get/set、日志、校验、权限判断)。传统做法要么靠继承硬耦合,要么复制粘贴写冗余代码,维护起来堪称"灾难"。

最近我琢磨出一套原创设计模式------「功能归一化」,无需继承、无虚函数、轻量无侵入,既能实现公共逻辑的统一管控,又能保留业务类的独立性,甚至暗合Spring AOP的切面思想,用C++原生特性就实现了"顶层统筹、底层自由"的架构逻辑。

今天就带大家从零拆解这套模式,从代码实现到设计思想,再到现实类比,让你一看就懂、一用就会,彻底解决C++ OOP开发中的冗余与耦合问题。

一、先看核心代码:30行实现「功能归一化」核心

先上完整可运行代码,大家可以直观感受下这套模式的简洁性------两个完全独立的类A、B,共享同一套公共操作,却没有任何继承关系。

cpp 复制代码
#include <iostream>
#include <functional>

// 中间统筹类(核心:定大局、统规则)
class Mid {
public:
    // 归一化设置接口:固定签名U(U),模板支持任意类型
    template <typename U>
    void set(const std::function<U(U)>&> &fn, U &tar, const U &u) {
        // 公共前置逻辑(归一化):所有set操作必走
        std::cout << "修改" << std::endl;
        // 业务逻辑由外部传入,Mid只负责赋值
        tar = fn(u);
    }

    // 归一化获取接口:固定签名U(),模板支持任意类型
    template <typename U>
    U get(const std::function<U()&> &fn) {
        // 公共前置逻辑(归一化):所有get操作必走
        std::cout << "获取" << std::endl;
        // 业务逻辑由外部传入,Mid只负责返回结果
        return fn();
    }
};

// 实体类A(无继承、独立封装)
class A {
private:
    int YourVar; // 私有成员,封装隔离
public:
    void set(int n) {
        // 调用统一接口,只传入自身业务逻辑(lambda)
        Mid().set<int>([n](int u) {
            return u + n; // A类独有的业务逻辑
        }, YourVar, n);
    }
    int get() {
        // 调用统一接口,传入自身业务逻辑
        return Mid().get<int>([this] { return YourVar; });
    }
};

// 实体类B(无继承、独立封装,与A毫无关联)
class B {
private:
    int YourVar; // 私有成员,封装隔离
public:
    void set(int n) {
        // 调用同一套统一接口,传入B类独有的业务逻辑
        Mid().set<int>([n](int u) {
            return u + n; // B类独有的业务逻辑(可与A不同)
        }, YourVar, n);
    }
    int get() {
        return Mid().get<int>([this] { return YourVar; });
    }
};

// 测试代码
int main() {
    A a;
    a.set(10);
    std::cout << a.get() << std::endl; // 输出:修改 → 获取 → 20

    B b;
    b.set(20);
    std::cout << b.get() << std::endl; // 输出:修改 → 获取 → 40
    return 0;
}

运行结果完全符合预期,关键是:A和B没有任何继承关系、没有虚函数、没有互相依赖,却都走了相同的公共逻辑(打印"修改""获取")。这就是「功能归一化」的核心魔力------无耦合的统一管控

二、「功能归一化」核心思想:3句话讲透

很多人看代码会觉得"这不就是简单的函数封装吗?",其实不然。这套模式的核心,是我提炼的「功能归一化铁律」,也是它区别于传统封装、继承的关键:

  1. 模板泛型负责"类型可变":不管是int、double、string,还是自定义结构体、类对象,模板U都能完美适配,无需为不同类型重复编写接口(遵循C++编程规范,避免硬编码和魔法数字,提升代码通用性)。

  2. 中间类负责"规则固定":Mid类定死接口签名(set是U(U)、get是U())、执行流程(公共前置→业务逻辑→赋值/返回),所有业务类必须遵守这个规则,实现"接口归一、流程归一"。

  3. 业务类负责"逻辑自由":业务类只需要封装自身数据,通过lambda传入独有的业务逻辑,不用关心公共操作(日志、校验等),实现"封装隔离、逻辑独立"。

一句话浓缩:流程骨架永久固化,数据类型无限可变;外壳规则统一归一,内部业务自由定制

三、最精髓的设计细节:为什么set接口是U(U)?

这是很多人会疑惑的点:明明set操作只需要"传值赋值",为什么要设计成"传入U、返回U"的函数签名?这正是「功能归一化」最反直觉、最精妙的地方,也是我反复打磨后确定的最优设计。

1. 参数U:原料必须有

参数u就像"工厂原料",是业务逻辑的输入源------比如A类set(10),10就是原料,lambda里的u就是这个原料,没有原料,业务逻辑就无法执行。这是所有set操作的共性,必须保留。

2. 返回值U:赋值的硬性要求

C++有个铁律:void类型不能赋值给变量。如果我们把lambda设计成void(U),那么tar = fn(u)就会编译报错------因为fn(u)没有返回值,无法完成赋值。

而U(U)的签名,能保证lambda执行后返回一个U类型的值,刚好能赋值给目标变量tar,这是语法层面的必然要求,也是「功能归一化」能落地的基础。

3. 核心目的:定义"映射关系"

最关键的一点:function<U(U)> 不是为了"传值",而是为了定义"原料→成品"的映射关系。

实际开发中,赋值往往不是简单的tar = u,可能是复杂计算(比如tar = u * 2 + 5)、数据校验(比如tar = u > 100 ? 100 : u)、格式转换等。

用U(U)的lambda,就是把这种"复杂映射"交给业务类自己实现,Mid只负责"统一流程+最终赋值"------Mid定规矩,业务类填内容,完美解耦。

四、现实类比:3个例子看懂「功能归一化」

为了让大家更易理解,我用3个生活化的例子,对应这套模式的核心逻辑,看完瞬间通透:

类比1:行政体系------顶层统筹,基层执行

Mid类 = 省市一把手,不负责具体执行,只定全局规矩(比如所有部门办事必须走报备流程);

A类、B类 = 各个局、各个基层部门,彼此独立、各司其职(业务逻辑不同);

不管哪个部门,只要办"同类公共事务"(比如申请经费、上报数据),都必须走一把手定的统一流程------这就是"归一化",既保证了规范,又不干涉基层的具体工作。

类比2:《伪装者》樱花号爆破行动

Mid类 = 明楼(顶层调度者),定好"爆破樱花号"的全局目标和统一规则;

A类 = 军统,B类 = 共产党,两个阵营毫无隶属关系(无继承、无耦合);

但两者都服从明楼的统一调度,各自执行自己的战术(业务逻辑),最终共同完成同一个目标------这就是「功能归一化」的核心:无关联实体,统一管控、协同完成公共目标。

类比3:Spring AOP面向切面编程

Mid类 = AOP切面容器,里面的公共逻辑(打印"修改""获取")就是切面;

业务类的lambda = 目标方法,是核心业务逻辑;

和Spring AOP一样,「功能归一化」实现了"业务逻辑与公共逻辑的解耦"------不用修改业务代码,只要修改Mid类的公共逻辑,所有业务类都会自动生效(比如新增日志、校验,只改Mid即可)。

区别在于:Spring AOP依赖框架、反射和动态代理,而我们的「功能归一化」用C++原生的模板+lambda+function实现,轻量无依赖,无需额外引入框架,更适合C++原生开发场景。

五、与传统OOP的对比:为什么「功能归一化」更优雅?

我们拿传统"继承+多态"和「功能归一化」做个对比,高下立判:

对比维度 传统继承+多态 功能归一化
耦合度 高耦合:子类必须继承父类,父类修改会影响所有子类 零耦合:业务类无继承、无依赖,彼此独立
侵入性 高侵入:子类必须重写虚函数,修改原有类结构 无侵入:业务类无需修改,只需调用Mid接口
扩展性 差:新增公共逻辑,需修改所有子类 极强:新增公共逻辑,只改Mid类,全局生效
性能 有虚函数开销,多态调用效率略低 无虚函数、无额外开销,轻量高效
适用场景 子类与父类有明确"is-a"关系 多个独立类需共享公共操作(get/set、日志等)

传统OOP的继承多态,更像"死板的上下级关系",必须从属绑定;而「功能归一化」更像"现代化统筹治理",各业务类高度自治,顶层统一规范,灵活又规整。

六、实际工程价值:哪些场景适合用?

这套模式不是"炫技",而是真正能解决工程痛点的实用设计,只要满足"多个类有公共操作、需要统一管控",都能直接套用,比如:

  1. 实体类的get/set统一管控:像示例中那样,多个实体类的读写操作,统一加日志、校验、权限判断,不用重复编写。

  2. 数据库/网络请求统一封装:所有数据库操作统一加连接、断开逻辑,所有网络请求统一加请求头、超时处理、异常捕获,只改Mid类即可全局生效。

  3. 文件/资源操作统一管控:所有文件读写统一加权限校验、打开/关闭流程,所有资源(内存、句柄)统一加计数、释放逻辑,避免资源泄漏。

  4. 团队开发统一编码规范:通过Mid类固定接口签名和执行流程,避免多人开发时写法混乱(比如有人写getX(),有人写fetchX()),天然约束代码风格,降低维护成本。

尤其适合中大型项目------后期需求变更时,比如"所有修改操作加数据备份",只需要在Mid的set接口中加一行代码,所有业务类自动生效,不用逐个修改,极大提升开发和维护效率,避免出现"改一处崩一片"的问题。

七、进阶优化:升级为工业级版本

示例代码是核心简化版,实际工程中可以在此基础上优化,让它更健壮、更通用,推荐3个进阶方向(后续会单独写文章详细实现):

  1. Mid类改为单例:避免每次调用都创建临时对象,提升效率(需保证线程安全,可采用双重检查锁定模式,兼顾线程安全和效率)。

  2. 新增异常捕获:在Mid的接口中加入try-catch,统一捕获业务逻辑中的异常,避免程序崩溃,同时统一打印异常日志。

  3. 扩展更多归一化接口:除了get/set,新增delete、save、query等接口,固定签名规则,实现增删改查全流程归一化;同时支持切面组合,实现多维度公共逻辑增强(如日志+耗时统计)。

八、总结:原创设计的核心价值

「功能归一化」不是传统23种设计模式的变种,而是我结合C++特性、工程痛点,以及现实中的统筹逻辑,原创的一套轻量化架构设计思想。

它的核心价值,在于用最简单的原生特性,解决了OOP开发中"冗余、耦合、维护难"的核心痛点------既保留了面向对象的封装特性,又实现了公共逻辑的统一管控,还避免了继承多态的弊端。

更重要的是,它门槛极低,不用掌握复杂的框架和高级特性,只要懂模板、lambda和function,就能快速上手,直接应用到实际项目中。

最后留个思考:你在开发中遇到过哪些"重复公共操作"的痛点?欢迎在评论区留言,我们一起用「功能归一化」解决!

后续会继续更新进阶版本(单例+线程安全+异常捕获+多切面扩展),关注我,一起打磨更优雅的C++代码~

相关推荐
FrontAI2 小时前
Next.js从入门到实战保姆级教程:实战项目(上)——全栈博客系统架构与核心功能
开发语言·前端·javascript·react.js·系统架构
zhangzeyuaaa2 小时前
深入 Python 模块与包:从自定义到标准库,再到第三方库的完全指南
开发语言·python
上海合宙LuatOS2 小时前
LuatOS扩展库API——【exvib】震动检测
开发语言·物联网·lua·luatos
freewlt2 小时前
Rust在前端工具链的崛起:2026年生态全景
开发语言·前端·rust
Java面试题总结2 小时前
Java常见面试题(160道)
java·开发语言
Rsun045512 小时前
7、Java 装饰器模式从入门到实战
java·开发语言·装饰器模式
fengci.2 小时前
php反序列化(复习)(第五章)
android·开发语言·学习·php
Q741_1472 小时前
每日一题 力扣 2515.到目标字符串的最短距离 循环数组 C++题解
c++·算法·leetcode
AI瓦力2 小时前
PDFBox处理JPEG2000图像报错解决方案(PDF扫描件)
开发语言