桥接模式中Abstraction(如OrderService)定义业务语义操作,Implementor(如OrderStorage)封装技术实现细节;关键在于按变化原因分离:业务逻辑稳定为抽象层,存储机制多变则归为实现层,通过组合而非继承解耦,运行时依赖注入切换具体实现。

桥接模式里 Abstraction 和 Implementor 怎么划分才不混淆
关键在识别「谁变、为什么变」。业务逻辑(比如订单创建、库存扣减)是稳定抽象层,它不该知道数据存在 MySQL 还是 Redis;而存储细节(连接管理、SQL 拼接、序列化方式)才是可变实现层。一旦把 OrderService 直接继承 MySQLOrderDao,就锁死了——这不是桥接,是继承耦合。
正确做法是让 OrderService 持有 OrderStorage 接口引用,而 MySQLOrderStorage、RedisOrderStorage 各自实现该接口。这样新增 MongoOrderStorage 时,OrderService 完全不用改。
-
Abstraction(如OrderService)只定义「我要存订单」「我要查订单ID」这类语义操作 -
Implementor(如OrderStorage)只定义「怎么写入键值对」「怎么执行 INSERT」这类技术动作 - 抽象层调用实现层时,传参应保持业务语义(如
Order对象),而非技术结构(如Map)
MySQL 和 NoSQL 实现类的差异点必须封装在 ConcreteImplementor 内部
MySQL 需要事务、主键生成、JOIN 查询;Redis 关注 TTL、哈希结构、原子计数器;MongoDB 侧重嵌套文档和索引策略。这些差异不能泄露到 OrderService,否则桥接就失效了。
例如:库存扣减在 MySQL 中用 UPDATE ... SET stock = stock - 1 WHERE id = ? AND stock > 0,在 Redis 中用 DECRBY + EXISTS 判断。这些 SQL 或命令字符串、重试逻辑、失败回滚方式,全部封在各自 ConcreteImplementor 的 decreaseStock() 方法里。
- MySQL 实现类需处理
SQLException并转为统一的StorageException - NoSQL 实现类若依赖序列化(如 Jackson),必须在构造时注入,不可硬编码
- 避免在实现类中做跨存储的兜底逻辑(比如 Redis 失败自动切 MySQL)——这会让抽象层隐式承担路由职责
运行时切换存储引擎的关键是依赖注入而非硬编码 new
如果代码里写死 new MySQLOrderStorage(),再好的桥接结构也白搭。切换引擎必须靠容器或工厂在启动时决定实例类型。
Spring 环境下,推荐用 @Profile 或配置项驱动 Bean 注册:
@Bean
@ConditionalOnProperty(name = "storage.type", havingValue = "mysql")
public OrderStorage mysqlStorage() {
return new MySQLOrderStorage(dataSource);
}
<p>@Bean
@ConditionalOnProperty(name = "storage.type", havingValue = "redis")
public OrderStorage redisStorage(RedisTemplate template) {
return new RedisOrderStorage(template);
}
</p>
- 绝对不要在业务方法里写
if ("redis".equals(type)) { ... }—— 这等于把桥接退化成 if-else - 配置项名(如
storage.type)应统一定义在常量类,避免拼写错误导致加载失败 - 本地开发时建议默认启用内存实现(如
InMemoryOrderStorage),避免每次启动都连真实 DB
容易被忽略的边界:事务一致性与查询能力落差
桥接能帮你替换存储,但无法抹平底层能力鸿沟。MySQL 支持 ACID 事务,Redis 只能靠 Lua 脚本模拟,MongoDB 的多文档事务有性能代价。这些不是设计模式能解决的,得在 OrderService 的契约里明确约束。
比如「创建订单并扣库存」这个操作,在 MySQL 实现里可放一个事务;在 Redis 实现里就得拆成两步,并接受「订单创建成功但库存扣减失败」的中间态——这个语义差异必须由上层业务兜底,不能指望桥接自动对齐。
- 所有实现类的
save()方法,必须文档化其隔离级别与失败场景(如「Redis 实现不保证幂等」) - 涉及 JOIN 或复杂聚合的查询,NoSQL 实现类可直接抛
UnsupportedOperationException,而不是强行模拟 - 测试时必须对每个
ConcreteImplementor单独跑完整用例,尤其验证异常路径是否一致











