在开发 EasyDSS 项目中,随着功能的扩大,慢慢的发现有很多类似的代码存在于代码中。因为结构体的不同,使用相同的函数进行合并非常困难,使用反射就可以将这些代码合并。

代码量越少,出现 bug 的概率则越低,因此对于相似的函数最好进行合并,用来降低代码量。以下文章中介绍如何使用反射来将部分代码合并的技巧。

首先需要了解什么是反射。在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。但在 Go 语言中并未完全实现反射的所有功能, Go 语言提供了一种机制在运行时更新变量和检查它们的值、调用它们的方法,但是在编译时并不知道这些变量的具体类型,这称为反射机制。

以下代码为判断一个用户为id的数据在数据库中是否存在的功能。

func (impl *BaseDaoImpl) Exists0(id string) error {
   data := table.User{}
   return impl.fromTable().First(&data, impl.WherePrimaryKey, id).Error
}

判断日志是否存在的功能

func (impl *BaseDaoImpl) Exists1(id string) error {
   data := table.ActiveLog{}
   return impl.fromTable().First(&data, impl.WherePrimaryKey, id).Error
}

判断产品是否存在的功能

func (impl *BaseDaoImpl) Exists3(id string) error {
   data := table.Product{}
   return impl.fromTable().First(&data, impl.WherePrimaryKey, id).Error
}

由以上代码就可以看出,三者完全一致,仅仅是 data 的类型不同。如果项目中此种代码比较少的话,没什么问题,但是随着功能的渐渐增多,此种代码就越来越多,代码冗余了,因此可以把三个代码进行合并成一个。

func (impl *BaseDaoImpl) Exists4(data interface{}, id string) error {
   return impl.fromTable().First(data, impl.WherePrimaryKey, id).Error
}

interface{} 为任意类型的结构体。

调用方法如下:
impl.Exists4(&table.User{}, id)
impl.Exists4(&table.ActiveLog{}, id)
impl.Exists4(&table.Product{}, id)

该种修改方式,降低了代码的冗余量,但是需要将函数的参数进行修改,增加一个 interface{} 任意类型的参数,并且在每次进行函数调用时,都需要将对应的结构体传入进入,因此还可以使用反射进行优化。

经过反射优化后的代码如下:

func (impl *BaseDaoImpl) Exists(id string) error {
   dataType := reflect.TypeOf(impl.TableStruct)
   data := reflect.New(dataType)
   return impl.fromTable().First(&data, impl.WherePrimaryKey, id).Error
}

impl.TableStruct 为 BaseDaoImpl 初始化的时候传入进来的结构体对象。在代码运行过程中,如果直接使用该对象,该对象内部的数据就会被改变,上一次的数据有可能影响这一次数据的查询。比如 TableStruct 为 user,开始 user.ID = 100, user.Name = “sam”,等到下一次使用该对象时,需要重新赋值。如果不重新赋值,则会出现以下情况。比如查询 id 为 200 的数据,采用 gorm 框架查询数据,最终发送的数据库语句就为select * where (user.id = 100) and id = 200 from users
就是查询 id 为 100,并且 id 为 200 的数据,显然此种数据是不存在的。因此每次查询前重新建一个内存数据查询就不会影响查询结果。

reflect 包就是 Go 语言中的反射包,其中reflect.TypeOf(impl.TableStruct) 就是查看传递进来的实例对象的数据类型,然后知道类型后,就调用 reflect.New(dataType) 就在内存中新建了一个新的对象,操作该未被使用过的对象,查询结果就会正确。

使用反射的方法,不用修改原先函数的参数,也消除了重复代码的存在,但是因为经过了一层反射,所以速度肯定没有Exists4函数快速,但是一般情况下,该段的速度消耗不需要考虑,因为推荐经过使用反射优化重复代码,简化代码,降低代码 bug 率。

本文是基于EasyDSS的开发过程给大家做个简单的说明科普,EasyDSS的开发过程复杂,可以用于各种不同场景的视频监控系统搭建,并且支持H265编码视频的播放,欢迎大家来进行测试使用。

posted on 2020-10-30 17:55  EasyDSS  阅读(105)  评论(0编辑  收藏  举报