首页/开发/代码质量与评审速查/OCP 实践:插件与注册机制
设计原则与模式62 条

OCP 实践:插件与注册机制

把扩展点做成显式注册:启动时扫描/登记实现(插件式),运行时按标识选择;新增能力以新增模块 + 注册条目完成,不触碰既有调用路径。

OCP 实践:插件与注册机制

开闭原则的工程化形态是把扩展点做成显式注册:启动时扫描或登记实现(插件式),运行时按标识选择。新增能力以「新增模块加一条注册条目」完成,不触碰既有调用路径。关键前提是扩展点稳定——接口与注册协议变更时,所有实现都要跟着改,所以它适合已经出现或明确预期的变化。

基本信息

形态插件目录、注册表、依赖注入集合、语言级服务加载
纪律扩展不改核心,注册点唯一
风险隐式发现顺序与冲突需要明确约定
语言机制Java 用 ServiceLoader 与 META-INF/services,其它生态各有插件约定

落地要点

  • 扩展点接口保持最小:只暴露选择器需要的成员
  • 注册集中在一处,避免多个模块各自维护一半注册表
  • 实现之间不得互相依赖,避免插件加载顺序影响行为
  • 冲突策略明确:同标识注册两次是报错、覆盖还是取第一个
  • 插件加载要有可观测日志:加载了哪些、版本多少、跳过原因
  • 扩展点带版本约束:插件与核心版本不匹配时给出清晰提示

注册示意(伪代码)

注册示意(伪代码)
for plugin in scan('plugins/*.jar'):
    registry.register(plugin.id(), plugin)   # 注册点唯一

export_service(registry.lookup('csv'))       # 按标识选择实现

# Java 生态对应机制
# ServiceLoader.load(ExportService.class)
# 提供方在 META-INF/services 下声明实现类全限定名

扩展机制选择

机制适用注意
插件目录扫描二进制可扩展,独立部署加载顺序与安全边界
显式注册表同一进程内扩展冲突策略要约定
依赖注入集合框架内按接口注入多实现启动期失败要清晰
语言级服务加载标准机制,跨模块发现需要提供方声明文件
插件加载没有可观测日志时,问题排查等于黑盒:加载失败、被跳过、版本不匹配都只在运行结果上体现。启动时打印一份已加载清单,成本很低,省下的排查时间很多。
扩展点没有版本约束时,核心与插件会慢慢脱节:核心改了接口语义,旧插件还在按老约定工作,问题只在特定组合下出现。约定兼容范围并做启动期校验,比事后排查便宜。

易错点

  • 插件加载无可观测日志:问题排查如黑盒,只能靠反复试
  • 扩展点无版本约束:插件与核心演进步调不一,特定组合下才暴露
  • 注册点分散在多个模块:新增实现要改多处,扩展成本回到修改核心
  • 为假想扩展提前抽象:扩展点没人实现,接口与注册机制成为负担

数据来源与参考

  • Meyer(1988)OCP 与插件化架构实践(Java SPI、OSGi 等生态文档)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌