策略模式与分布式软总线连接模块

策略模式与分布式软总线连接模块 概述策略模式是连接模块的骨架策略模式Strategy Pattern是分布式软总线连接模块的第一架构模式——它决定了整个模块的骨架结构。简单来说策略模式解决的核心问题是同一件事有多种做法需要在运行时灵活选择用哪种做法。在连接模块中连接设备这件事有三种做法——通过蓝牙BR、通过蓝牙BLE、通过TCP将来还可能新增WiFi Direct等。策略模式让连接管理器不需要知道每种连接具体怎么实现只需要定义一套统一的接口契约然后让各连接类型各自实现。调用时根据连接类型自动路由到正确的实现。这就好比你去餐厅点餐菜单ConnectFuncInterface定义了炒菜这个动作但后厨有川菜师傅BR、粤菜师傅BLE、鲁菜师傅TCP你点什么口味就派给对应的师傅来做——你不需要亲自跑到后厨去指定。一、策略模式原理详解1.1 模式定义策略模式Strategy Pattern又称政策模式Policy Pattern属于GoF 23种设计模式中的行为型模式。核心思想定义一组算法策略将每一个算法封装到具有共同接口的独立类中使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户。用大白话说就是把做什么和怎么做分开。做什么由上下文Context决定怎么做由具体策略Strategy决定两者通过接口解耦。1.2 三个核心角色角色英文名中文解释职责策略接口Strategy策略/抽象策略定义所有支持的算法的公共接口声明统一的方法签名是所有具体策略的契约具体策略ConcreteStrategy具体策略实现策略接口中定义的算法每个具体策略封装一种具体的算法实现上下文Context环境/上下文持有策略引用将请求委托给策略维护对策略对象的引用在运行时切换策略1.3 经典UML结构1.4 经典代码骨架伪代码// 1. 策略接口定义统一契约 typedef struct { int32_t (*algorithm)(int32_t input); // 算法接口 } Strategy; // 2. 具体策略A算法A的实现 int32_t AlgorithmA(int32_t input) { return input * 2; } Strategy strategyA { .algorithm AlgorithmA }; // 3. 具体策略B算法B的实现 int32_t AlgorithmB(int32_t input) { return input 10; } Strategy strategyB { .algorithm AlgorithmB }; // 4. 上下文持有策略引用运行时选择 Strategy *currentStrategy; void setStrategy(Strategy *s) { currentStrategy s; } int32_t doSomething(int32_t input) { return currentStrategy-algorithm(input); } // 5. 使用 setStrategy(strategyA); // 选择策略A result doSomething(5); // 调用 → 5 * 2 10 setStrategy(strategyB); // 切换到策略B result doSomething(5); // 调用 → 5 10 151.5 策略模式的核心价值价值点说明消除条件分支不用写一堆if-else或switch-case用策略替换直接分发算法独立变化新增算法不影响已有算法和上下文运行时切换可以在程序运行过程中动态更换策略复用与组合同一策略可被多个上下文复用二、连接模块中策略模式的完整实现2.1 角色映射三个角色如何对应经典角色连接模块对应具体代码Strategy策略接口ConnectFuncInterfacesoftbus_conn_manager.hConcreteStrategy具体策略BR策略、BLE策略、TCP策略ConnInitBr()、ConnInitBle()、ConnInitTcp()返回的对象Context上下文ConnManager连接管理器g_connManager[]数组 路由分发函数2.2 策略接口ConnectFuncInterface这是整个策略模式的灵魂——它定义了连接这件事到底包含哪些动作。所有连接类型必须实现这套接口typedef struct { int32_t (*ConnectDevice)(const ConnectOption *option, uint32_t requestId, const ConnectResult *result); int32_t (*PostBytes)(uint32_t connectionId, uint8_t *data, uint32_t len, ...); int32_t (*DisconnectDevice)(uint32_t connectionId); int32_t (*DisconnectDeviceNow)(const ConnectOption *option); int32_t (*GetConnectionInfo)(uint32_t connectionId, ConnectionInfo *info); int32_t (*StartLocalListening)(const LocalListenerInfo *info); int32_t (*StopLocalListening)(const LocalListenerInfo *info); bool (*CheckActiveConnection)(const ConnectOption *info, bool needOccupy); int32_t (*UpdateConnection)(uint32_t connectionId, UpdateOption *option); int32_t (*PreventConnection)(const ConnectOption *option, uint32_t time); int32_t (*ConfigPostLimit)(const LimitConfiguration *configuration); } ConnectFuncInterface;用大白话翻译每个方法方法含义生活类比ConnectDevice建立连接拨打电话PostBytes发送数据说话DisconnectDevice断开连接优雅说再见后挂电话DisconnectDeviceNow立即断开连接直接挂电话GetConnectionInfo查询连接信息查看通话记录StartLocalListening开始监听打开电话等待来电StopLocalListening停止监听关闭电话不再接听CheckActiveConnection检查是否有活跃连接查看是否在通话中UpdateConnection更新连接参数通话中调整音量PreventConnection阻止连接设置免打扰ConfigPostLimit配置流量限制设置流量套餐2.3 具体策略三种连接实现BR策略蓝牙经典连接在 softbus_conn_br_manager.c:1856 中填充static ConnectFuncInterface connectFuncInterface { .ConnectDevice BrConnectDevice, // BR方式建立连接 .PostBytes ConnBrPostBytes, // BR方式发送数据 .DisconnectDevice BrDisconnectDevice, // BR方式优雅断连 .DisconnectDeviceNow BrDisconnectDeviceNow, // BR方式立即断连 .GetConnectionInfo BrGetConnectionInfo, // BR方式查询信息 .StartLocalListening BrStartLocalListening, // BR方式开始监听 .StopLocalListening BrStopLocalListening, // BR方式停止监听 .CheckActiveConnection BrCheckActiveConnection, // BR方式检查活跃连接 .UpdateConnection NULL, // BR不支持更新 .PreventConnection BrPendConnection, // BR支持阻止连接 .ConfigPostLimit ConnBrTransConfigPostLimit, // BR流量控制 };BLE策略蓝牙低功耗连接在 softbus_conn_ble_manager.c:2360 中填充static ConnectFuncInterface bleFuncInterface { .ConnectDevice BleConnectDevice, .PostBytes ConnBlePostBytes, .DisconnectDevice BleDisconnectDevice, .DisconnectDeviceNow BleDisconnectDeviceNow, .GetConnectionInfo BleGetConnectionInfo, .StartLocalListening BleStartLocalListening, .StopLocalListening BleStopLocalListening, .CheckActiveConnection BleCheckActiveConnection, .UpdateConnection BleUpdateConnection, // BLE支持更新如调整优先级 .PreventConnection NULL, // BLE不支持阻止连接 .ConfigPostLimit ConnBleTransConfigPostLimit, };TCP策略WiFi局域网连接在 softbus_tcp_connect_manager.c:767 中填充static void InitTcpInterface(void) { g_tcpInterface.ConnectDevice TcpConnectDevice; g_tcpInterface.DisconnectDevice TcpDisconnectDevice; g_tcpInterface.DisconnectDeviceNow TcpDisconnectDeviceNow; g_tcpInterface.PostBytes TcpPostBytes; g_tcpInterface.GetConnectionInfo TcpGetConnectionInfo; g_tcpInterface.StartLocalListening TcpStartListening; g_tcpInterface.StopLocalListening TcpStopListening; g_tcpInterface.CheckActiveConnection TcpCheckActiveConnection; g_tcpInterface.UpdateConnection NULL; // TCP不支持更新 g_tcpInterface.PreventConnection NULL; // TCP不支持阻止连接 }关键观察三种策略对同一接口方法的实现完全不同而且有些方法设为NULL表示该连接类型不支持此操作。这正是策略模式的精髓——统一接口差异实现。2.4 上下文连接管理器的策略注册与路由2.4.1 策略注册初始化时绑定具体策略ConnectFuncInterface *g_connManager[CONNECT_TYPE_MAX] { 0 }; // 策略数组 int32_t ConnServerInit(void) { // ... 前置初始化 ... // 将三种具体策略注册到策略数组中 if (isSupportTcp) { g_connManager[CONNECT_TCP] ConnInitTcp(g_connManagerCb); // TCP策略 } g_connManager[CONNECT_BR] ConnInitBr(g_connManagerCb); // BR策略 g_connManager[CONNECT_BLE] ConnInitBle(g_connManagerCb); // BLE策略 }这就是策略模式的注册阶段——把川菜师傅粤菜师傅鲁菜师傅安排到后厨的对应工位上。2.4.2 策略路由运行时分发到正确策略连接管理器通过connectionId的高16位来识别连接类型从而路由到正确的策略#define CONNECT_TYPE_SHIFT 16 // connectionId (ConnectType 16) | 序号 // 例如BR连接ID (2 16) | 5 0x00020005路由分发示例——连接设备int32_t ConnConnectDevice(const ConnectOption *info, uint32_t requestId, const ConnectResult *result) { // 1. 类型校验 if (ConnTypeCheck(info-type) ! SOFTBUS_OK) { return ERROR; } // 2. 策略路由根据类型直接索引到对应策略调用其ConnectDevice方法 return g_connManager[info-type]-ConnectDevice(info, requestId, result); }路由分发示例——发送数据int32_t ConnPostBytes(uint32_t connectionId, ConnPostData *data) { // 1. 从connectionId提取连接类型 ConnectType type (connectionId CONNECT_TYPE_SHIFT); // 2. 策略路由直接索引到对应策略 return g_connManager[type]-PostBytes(connectionId,>int32_t ConnDisconnectDevice(uint32_t connectionId) { ConnectType type (connectionId CONNECT_TYPE_SHIFT); return g_connManager[type]-DisconnectDevice(connectionId); }2.5 完整的策略模式流转图2.6 与经典策略模式的差异连接模块的实现与教科书上的策略模式有一些务实的差异这些差异体现了嵌入式系统的工程智慧差异点经典策略模式连接模块实现原因策略数量通常2-3个运行时切换3个固定策略初始化时注册连接类型在系统生命周期内不变策略选择由Context决定用哪个由调用方ConnectType或连接ID决定调用方明确知道要用哪种连接策略存储单个策略指针策略数组g_connManager[MAX]需要同时管理多种策略接口实现所有方法必须实现允许NULL不支持的操作不是每种连接都支持所有操作语言机制C虚函数/接口继承C语言函数指针结构体系统级服务要求C语言、高性能三、为什么要使用策略模式3.1 如果不用策略模式会怎样让我们想象一下没有策略模式的代码会是什么样// ❌ 反面教材不用策略模式的实现 int32_t ConnConnectDevice(const ConnectOption *info, ...) { if (info-type CONNECT_TCP) { // TCP连接的100行逻辑... socket socket(AF_INET, SOCK_STREAM, 0); connect(socket, ...); // ... } else if (info-type CONNECT_BR) { // BR连接的200行逻辑... SppConnect(info-brOption.brMac); // 状态机处理... // ACL碰撞处理... // ... } else if (info-type CONNECT_BLE) { // BLE连接的300行逻辑... GattConnect(info-bleOption.bleMac); // 服务发现... // MTU协商... // 特征订阅... // ... } } int32_t ConnPostBytes(uint32_t connectionId, ...) { if (type CONNECT_TCP) { /* TCP发送逻辑 */ } else if (type CONNECT_BR) { /* BR发送逻辑 */ } else if (type CONNECT_BLE) { /* BLE发送逻辑 */ } } // 每个函数都要重复同样的 if-else 分支问题一目了然问题严重程度说明代码膨胀 严重每个函数都塞满所有连接类型的逻辑一个文件可能上万行修改扩散 严重修改BR连接逻辑要在一个巨大函数中找到对应分支容易改错新增困难 严重新增WiFi Direct连接要修改每一个函数的if-else无法独立测试 中等无法单独测试BR逻辑必须带着TCP和BLE一起编译编译依赖 中等所有连接类型的头文件互相纠缠3.2 使用策略模式后的收益收益1消除条件分支最直观的收益之前每个函数都有if-else分支判断类型之后一行代码直接路由// ✅ 策略模式一行搞定无需if-else return g_connManager[info-type]-ConnectDevice(info, requestId, result);这就是策略模式最核心的威力——用多态替代条件分支。收益2关注点分离每个策略独立开发BR开发工程师 ──→ 只看 br/ 目录不需要理解BLE和TCP BLE开发工程师 ──→ 只看 ble/ 目录不需要理解BR和TCP TCP开发工程师 ──→ 只看 tcp/ 目录不需要理解BR和BLE每个连接类型是完全独立的编译单元有自己的状态机ConnBrState/ConnBleState连接实例ConnBrConnection/ConnBleConnection数据传输ConnBrTrans/ConnBleTrans发送队列ConnBrSendQueue/ConnBleSendQueue收益3开放封闭原则OCP—— 新增连接类型零修改假设未来要新增一种NFC连接只需要// 第1步实现NFC策略新增文件不修改任何现有文件 static ConnectFuncInterface nfcFuncInterface { .ConnectDevice NfcConnectDevice, .PostBytes NfcPostBytes, .DisconnectDevice NfcDisconnectDevice, // ... }; // 第2步在初始化中注册仅增加一行 g_connManager[CONNECT_NFC] ConnInitNfc(g_connManagerCb);不需要修改❌ 不改ConnectFuncInterface接口定义❌ 不改ConnConnectDevice()路由逻辑❌ 不改 BR/BLE/TCP 的任何代码❌ 不改上层业务调用代码这就是对扩展开放对修改封闭。收益4接口契约的约束力ConnectFuncInterface不仅是策略接口更是契约——它强制要求每种连接类型必须实现核心方法ConnectDevice、PostBytes、DisconnectDevice等否则注册时会设为NULL调用时会被拦截if (g_connManager[type]-PostBytes NULL) { return SOFTBUS_CONN_MANAGER_OP_NOT_SUPPORT; // 明确告知不支持 }这比if-else方式更安全——不支持的操作有明确的错误码返回而不是遗漏分支导致未定义行为。收益5运行时能力探测策略模式天然支持能力探测——通过检查策略接口的函数指针是否为NULL可以知道某种连接类型支持哪些操作操作BRBLETCPConnectDevice✅✅✅PostBytes✅✅✅DisconnectDevice✅✅✅UpdateConnection❌✅❌PreventConnection✅❌❌ConfigPostLimit✅✅❌这种差异化能力在if-else方式中很难优雅地表达。3.3 为什么用C函数指针而非C虚函数这是很多人会问的问题。连接模块选择C语言函数指针实现策略模式有深层的技术考量对比维度C虚函数C函数指针运行时开销vtable间接寻址2次指针解引用直接函数指针调用1次解引用内存布局每个对象额外4/8字节vptr无额外开销ABI稳定性受编译器影响vtable布局可能不同函数指针布局稳定兼容性需要C运行时支持纯C无运行时依赖内核态可用❌ 内核通常不支持C✅ 内核广泛使用函数指针调试可读性虚函数调用栈不够直观函数指针名称可打印结论对于分布式软总线这种系统级基础服务C函数指针是比C虚函数更合适的选择——更轻量、更稳定、更通用。3.4 策略模式与观察者模式的协同连接模块中策略模式和观察者模式不是孤立存在的而是协同工作形成完整的架构具体协同流程以BR连接建立为例1. 上层调用 ConnConnectDevice(BR类型) → 策略模式路由到 g_connManager[BR]-ConnectDevice() → BrConnectDevice() 执行BR连接逻辑 2. BR连接成功后调用 g_connManagerCb.OnConnected() → ConnManagerConnected() 触发观察者模式 → 遍历 g_listenerList通知所有业务模块 → 认证模块.OnConnected() → 启动认证 → 通道模块.OnConnected() → 创建通道 → 代理模块.OnConnected() → 调整策略策略模式负责往下走选择实现观察者模式负责往上走通知结果两者一上一下构成了完整的控制流和数据流。总结策略模式的设计精髓核心价值一句话策略模式让连接模块实现了一套接口、多种实现、按需路由——上层只管我要连接不用管怎么连接底层各自实现怎么连接不用管谁在调用。设计原则落地总结设计原则策略模式如何落地开闭原则OCP新增连接类型只需新增策略实现注册一行零修改现有代码单一职责SRP每种连接策略只负责自己的连接逻辑管理器只负责路由里氏替换LSP所有策略都可替换上层代码不依赖具体策略接口隔离ISPConnectFuncInterface只包含连接相关方法不掺杂业务逻辑依赖倒置DIP管理器依赖抽象接口ConnectFuncInterface不依赖具体BR/BLE/TCP实现与现实生活的类比把连接模块想象成一个快递公司角色快递公司连接模块策略接口配送这个动作ConnectFuncInterface具体策略陆运/空运/海运三种配送方式TCP/BR/BLE三种连接方式上下文调度中心根据包裹类型选择配送方式ConnManager根据ConnectType路由观察者客户收到配送通知业务模块收到连接事件回调调度中心上下文不需要知道陆运怎么开车、空运怎么开飞机只需要知道配送这个动作——这就是策略模式的本质把做什么和怎么做彻底分开。