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

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

背景

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

具体来看,该方法通过在函数内部直接编写条件判断语句实现查询拼接,尽管从面向对象设计的开闭原则(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 函数通过遍历这个切片来应用这些查询函数。当需要添加新的查询条件时,只需添加一个新的匿名函数到切片中。

优势

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

总结

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

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

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

相关文章:

  • 再学GPIO(三)
  • transform-实现Encoder 编码器模块
  • HHsuite同源序列搜索数据库构建
  • 混淆矩阵(Confusion Matrix)横坐标
  • GrapesJS 终极定制组件设计方案:扁平化对象属性编辑、多区域拖拽、多层嵌套与组件扩展实战
  • 在大数据环境下,使用spingboot为Android APP推送数据方案
  • 【进程与线程】
  • 微信小程序 首页之轮播图和搜索框 代码分享
  • 支付行业的前景如何
  • AimRT 从零到一:官方示例精讲 —— 四、logger示例.md
  • 【Java核心】一文理解Java面向对象(超级详细!)
  • C++之string
  • 手动安装OpenSSL1.1.1
  • 亮数据:AI时代的数据采集革命者——从试用体验到实战应用全解析
  • VIT(ICLR2021)
  • 建立对人工智能(AI)的信任
  • PCB入门指南:从电阻到常见电路的全解析
  • redis高阶2 高性能
  • 1.7 点云数据获取方式——视觉SLAM
  • 深度循环神经网络
  • 广东省副省长刘红兵任湖南省委常委、宣传部部长
  • 交行一季度净利253.72亿元增1.54%,不良率微降
  • 解读|特朗普“助攻”下加拿大自由党“惨胜”,卡尼仍需克服“特鲁多阴影”
  • 阿里开源首个“混合推理模型”:集成“快思考”、“慢思考”能力
  • 从咖啡节到话剧、演唱会,上海虹口“文旅商体展”联动促消费
  • 华侨城A:一季度营收53.63亿元,净利润亏损14.19亿元