当前位置: 首页 > ai >正文

基于开闭原则优化数据库查询语句拼接方法

背景

在开发实践中,曾有同事在实现新功能时,因直接修改一段数据库查询条件拼接方法的代码逻辑,导致生产环境出现故障。

具体来看,该方法通过在函数内部直接编写条件判断语句实现查询拼接,尽管从面向对象设计的开闭原则(OCP)出发,理想的代码应满足 “对修改封闭、对扩展开放”—— 即允许通过扩展而非修改原有逻辑来应对变化,但这一规范属于非强制性设计原则,在实际开发中难以确保所有成员始终严格遵守,从而导致新增或调整查询条件时,开发人员更倾向于直接修改原函数,而非通过扩展方式实现,最终埋下代码变更的风险隐患。

func (m ActivityModel) buildQuery(ctx context.Context, db *gorm.DB, filter *ActivityListFilter) {if filter.Name != "" {db = db.Where("name like ?", "%"+filter.Name+"%")}if filter.StartAt > 0 {db = db.Where("start_at <= ?", filter.StartAt)}if filter.EndAt > 0 {db = db.Where("end_at >= ?", filter.EndAt)}if filter.Description != "" {db = db.Where("description like ?", "%"+filter.Description+"%")}if filter.CreatedBy != "" {db = db.Where("created_by like ?", "%"+filter.CreatedBy+"%")}db = db.Where("is_del = ?", filter.IsDel)if filter.CreatedAt > 0 {db = db.Where("create_time > ?", filter.CreatedAt)}if filter.UpdatedAt > 0 {db = db.Where("update_time > ?", filter.UpdatedAt)}if filter.DeletedAt > 0 {db = db.Where("delete_time > ?", filter.DeletedAt)}
}

上述代码中,每个查询条件的添加或修改都需直接操作 buildQuery 函数,违背了开闭原则。为降低维护风险并提升代码扩展性,可通过设计模式将查询条件的逻辑解耦,实现 “对扩展开放,对修改封闭” 的目标。

方案一:使用策略模式

策略模式可以将每个查询条件封装成独立的策略,这样在需要添加新的查询条件时,只需新增一个策略类,而无需修改原有的代码。

package mainimport ("context""github.com/jinzhu/gorm"
)// ActivityModel 定义活动模型
type ActivityModel struct{}// ActivityListFilter 定义过滤条件
type ActivityListFilter struct {Name        stringStartAt     int64EndAt       int64Description stringCreatedBy   stringIsDel       boolCreatedAt   int64UpdatedAt   int64DeletedAt   int64
}// QueryStrategy 定义查询策略接口
type QueryStrategy interface {Apply(db *gorm.DB, filter *ActivityListFilter) *gorm.DB
}// NameQueryStrategy 实现名称查询策略
type NameQueryStrategy struct{}func (n NameQueryStrategy) Apply(db *gorm.DB, filter *ActivityListFilter) *gorm.DB {if filter.Name != "" {return db.Where("name like ?", "%"+filter.Name+"%")}return db
}// StartAtQueryStrategy 实现开始时间查询策略
type StartAtQueryStrategy struct{}func (s StartAtQueryStrategy) Apply(db *gorm.DB, filter *ActivityListFilter) *gorm.DB {if filter.StartAt > 0 {return db.Where("start_at >= ?", filter.StartAt)}return db
}// 可以继续为其他条件实现类似的策略// buildQuery 使用策略模式构建查询
func (m ActivityModel) buildQuery(ctx context.Context, db *gorm.DB, filter *ActivityListFilter) *gorm.DB {strategies := []QueryStrategy{NameQueryStrategy{},StartAtQueryStrategy{},// 添加其他策略}for _, strategy := range strategies {db = strategy.Apply(db, filter)}return db.Where("is_del = ?", filter.IsDel)
}

在这个方案中,每个查询条件都被封装成一个独立的策略,buildQuery 函数通过遍历策略列表来应用这些策略。当需要添加新的查询条件时,只需实现一个新的策略类并将其添加到策略列表中。

优势

  • 解耦条件逻辑:每个策略类单一职责,聚焦特定条件处理,降低代码耦合度。
  • 无缝扩展:新增查询条件时,只需实现 QueryStrategy 接口并添加到策略列表,无需修改核心逻辑。
  • 便于测试:可独立单元测试每个策略,提升测试覆盖率和维护效率。

方案二:使用函数切片

你可以将每个查询条件封装成一个函数,并将这些函数存储在一个切片中。这样,在需要添加新的查询条件时,只需添加一个新的函数到切片中。

package mainimport ("context""github.com/jinzhu/gorm"
)// ActivityModel 定义活动模型
type ActivityModel struct{}// ActivityListFilter 定义过滤条件
type ActivityListFilter struct {Name        stringStartAt     int64EndAt       int64Description stringCreatedBy   stringIsDel       boolCreatedAt   int64UpdatedAt   int64DeletedAt   int64
}// QueryFunc 定义查询函数类型
type QueryFunc func(db *gorm.DB, filter *ActivityListFilter) *gorm.DB// buildQuery 使用函数切片构建查询
func (m ActivityModel) buildQuery(ctx context.Context, db *gorm.DB, filter *ActivityListFilter) *gorm.DB {queryFuncs := []QueryFunc{func(db *gorm.DB, filter *ActivityListFilter) *gorm.DB {if filter.Name != "" {return db.Where("name like ?", "%"+filter.Name+"%")}return db},func(db *gorm.DB, filter *ActivityListFilter) *gorm.DB {if filter.StartAt > 0 {return db.Where("start_at >= ?", filter.StartAt)}return db},// 添加其他查询函数}for _, queryFunc := range queryFuncs {db = queryFunc(db, filter)}return db.Where("is_del = ?", filter.IsDel)
}

在这个方案中,每个查询条件都被封装成一个匿名函数,并存储在 queryFuncs 切片中。buildQuery 函数通过遍历这个切片来应用这些查询函数。当需要添加新的查询条件时,只需添加一个新的匿名函数到切片中。

优势

  • 轻量简洁:无需定义额外接口或类,直接通过匿名函数实现条件封装,适合简单场景。
  • 灵活组合:可动态增删条件函数,支持运行时根据业务需求调整查询逻辑。
  • 代码隔离:每个条件逻辑在独立函数内实现,修改单个条件不会影响其他逻辑。

总结

这两种方案都遵循了开闭原则,使得代码在添加新的查询条件时更加灵活,同时减少了修改现有代码的风险。

  • 策略模式适合条件逻辑复杂、需要多维度扩展或复用的场景,通过接口化设计提升代码规范性。
  • 函数切片则以更轻量的方式实现条件解耦,适合快速开发或条件相对固定的场景。

无论选择哪种方案,核心目标都是将查询条件的 “修改” 操作转化为 “扩展” 操作 —— 新增条件时无需触碰原有逻辑,从架构层面降低人为失误导致的风险。

http://www.xdnf.cn/news/2933.html

相关文章:

  • KenticoCMS 文件上传导致xss漏洞复现(CVE-2025-2748)
  • RN 获取视频封面,获取视频第一帧
  • 【免费下载】1985-2023年全国土地利用数据
  • 《算法笔记》10.5小节——图算法专题->最小生成树 问题 B: Freckles
  • 当前HPLC载波无法满足全量数据分钟级采集需求的主要原因
  • STM32 SPI通信协议
  • 从整体上把握操作系统的作用,以及理解进程状态是什么
  • EtherCAT转Profinet网关,包装产线的“语言翻译器”
  • python:练习:2
  • 查看Mysql版本
  • c/c++之信号处理<signal.h>
  • 【vue3】黑马程序员前端Vue3小兔鲜电商项目【五】
  • 问题排查:calss extends 后页面加载不出来(忘记加super),打包后不报错;遇到问题可以适当出去走一下,让脑子休息一下
  • AimRT 从零到一:官方示例精讲 —— 五、Parameter示例.md
  • WPF(Windows Presentation Foundation)的内容模型
  • 可视化图解算法: 判断是不是二叉搜索树(验证二叉搜索树)
  • SEO优化指南与实战技巧
  • centos安装部署配置kafka
  • Vue常用的修饰符有哪些有什么应用场景(含deep seek讲解)
  • 通用事件库IO多路复用技术选型与设计
  • 常见位运算总结
  • 塑料材料工程师简历模板
  • C#进阶学习(十七)PriorityQueue<TElement, TPriority>优先级队列的介绍
  • 阿里云服务器 篇十二:加入 Project Honey Pot 和使用 http:BL
  • 万象生鲜配送系统代码2025年4月29日更新日志
  • Java练习3
  • c语言的常用的预处理指令和条件编译
  • __proto__与prototype
  • 误在非开发分支上开发解决方案
  • LabVIEW实验室项目中使用类模块与仿真