Skip to content

Commit

Permalink
copier: ReflectCopier 支持忽略字段 (#196)
Browse files Browse the repository at this point in the history
* bean/cpoier add ignore option

* bean/copier,1、使用 bean/option 和 set 改造一下忽略字段的需求;2、重新给测试用例命名;

* bean/copier,将 options 改成 option 模式的,方便后续别的附加功能可以自定义初始化过程。

* bean/copier,将 options 改成延迟初始化。

* bean/copier,执行 make check;修改 mapset 初始化时的容量;补全测试用例;
  • Loading branch information
KelipuTe authored Jul 5, 2023
1 parent 0f2c145 commit 72ded6a
Show file tree
Hide file tree
Showing 4 changed files with 596 additions and 5 deletions.
1 change: 1 addition & 0 deletions .CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- [mapx: MutipleTreeMap](https://github.com/ecodeclub/ekit/pull/187)
- [mapx: 为 MultipleMap 添加 PutVals 方法](https://github.com/ecodeclub/ekit/pull/189)
- [mapx: LinkedMap 特性](https://github.com/ecodeclub/ekit/pull/191)
- [copier: ReflectCopier 支持忽略字段](https://github.com/ecodeclub/ekit/pull/196)

# v0.0.7
- [slice: FilterDelete](https://github.com/ecodeclub/ekit/pull/152)
Expand Down
44 changes: 42 additions & 2 deletions bean/copier/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,54 @@

package copier

import (
"github.com/ecodeclub/ekit/bean/option"
"github.com/ecodeclub/ekit/set"
)

// Copier 复制数据
// 1. 深拷贝亦或是浅拷贝,取决于具体的实现。每个实现都要声明清楚这一点;
// 2. Src 和 Dst 都必须是普通的结构体,支持组合
// 3. 只复制公共字段
// 这种设计设计,即使用 *Src 和 *Dst 可能加剧内存逃逸
type Copier[Src any, Dst any] interface {
// CopyTo 将 src 中的数据复制到 dst 中
CopyTo(src *Src, dst *Dst) error
CopyTo(src *Src, dst *Dst, opts ...option.Option[options]) error
// Copy 将创建一个 Dst 的实例,并且将 Src 中的数据复制过去
Copy(src *Src) (*Dst, error)
Copy(src *Src, opts ...option.Option[options]) (*Dst, error)
}

// options 执行复制操作时的可选配置
type options struct {
// ignoreFields 执行复制操作时,需要忽略的字段
ignoreFields *set.MapSet[string]
}

func newOptions() *options {
return &options{}
}

// InIgnoreFields 判断 str 是不是在 ignoreFields 里面
func (r *options) InIgnoreFields(str string) bool {
// 如果没有设置过忽略的字段的话,ignoreFields 就有可能是 nil,这里需要判断一下
if r.ignoreFields == nil {
return false
}
return r.ignoreFields.Exist(str)
}

// IgnoreFields 设置复制时要忽略的字段(option 设计模式)
func IgnoreFields(fields ...string) option.Option[options] {
return func(opt *options) {
if len(fields) < 1 {
return
}
// 需要用的时候再延迟初始化 ignoreFields
if opt.ignoreFields == nil {
opt.ignoreFields = set.NewMapSet[string](len(fields))
}
for i := 0; i < len(fields); i++ {
opt.ignoreFields.Add(fields[i])
}
}
}
21 changes: 18 additions & 3 deletions bean/copier/reflect_copier.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ package copier

import (
"reflect"

"github.com/ecodeclub/ekit/bean/option"
)

// ReflectCopier 基于反射的实现
Expand All @@ -24,6 +26,9 @@ type ReflectCopier[Src any, Dst any] struct {

// rootField 字典树的根节点
rootField fieldNode

// options 执行复制操作时的可选配置
options *options
}

// fieldNode 字段的前缀树
Expand Down Expand Up @@ -142,9 +147,9 @@ func createFieldNodes(root *fieldNode, srcTyp, dstTyp reflect.Type) error {
return nil
}

func (r *ReflectCopier[Src, Dst]) Copy(src *Src) (*Dst, error) {
func (r *ReflectCopier[Src, Dst]) Copy(src *Src, opts ...option.Option[options]) (*Dst, error) {
dst := new(Dst)
err := r.CopyTo(src, dst)
err := r.CopyTo(src, dst, opts...)
return dst, err
}

Expand All @@ -154,7 +159,11 @@ func (r *ReflectCopier[Src, Dst]) Copy(src *Src) (*Dst, error) {
// 2. 如果 Src 和 Dst 中匹配的字段,其类型是基本类型(及其指针)或者内置类型(及其指针),并且类型一样,则直接用 Src 的值
// 3. 如果 Src 和 Dst 中匹配的字段,其类型都是结构体,或者都是结构体指针,则会深入复制
// 4. 否则,忽略字段
func (r *ReflectCopier[Src, Dst]) CopyTo(src *Src, dst *Dst) error {
func (r *ReflectCopier[Src, Dst]) CopyTo(src *Src, dst *Dst, opts ...option.Option[options]) error {
opt := newOptions()
option.Apply(opt, opts...)
r.options = opt

return r.copyToWithTree(src, dst)
}

Expand Down Expand Up @@ -191,6 +200,12 @@ func (r *ReflectCopier[Src, Dst]) copyTreeNode(srcTyp reflect.Type, srcValue ref

for i := range root.fields {
child := &root.fields[i]

// 只要结构体属性的名字在需要忽略的字段里面,就不走下面的复制逻辑
if r.options.InIgnoreFields(child.name) {
continue
}

childSrcTyp := srcTyp.Field(child.srcIndex)
childSrcValue := srcValue.Field(child.srcIndex)

Expand Down
Loading

0 comments on commit 72ded6a

Please sign in to comment.