Elegant way to eliminate multiple condition checks in function

huangapple go评论83阅读模式
英文:

Elegant way to eliminate multiple condition checks in function

问题

有多个函数中有多个条件检查。

type VA struct {
	A string
}

func (va *VA) CheckA(s string) error {
	if s != va.A {
		return errors.New("无效的字符串")
	}
	return nil
}

type VB struct {
	B int
}

func (vb *VB) CheckB(i int) error {
	if i == vb.B {
		return errors.New("无效的整数")
	}
	return nil
}

func FuncA(s string, i int) error {
	a := &VA{A: "testa"}
	errA := a.CheckA(s)
	if errA != nil {
		return errA
	}

	b := &VB{B: 3}
	errB := b.CheckB(i)
	if errB != nil {
		return errB
	}

	// 更多逻辑...

	return nil
}

func FuncB(sb string, v int32) error {
	a := &VA{A: "testb"}
	errA := a.CheckA(sb)
	if errA != nil {
		return errA
	}

	// 更多逻辑...

	return nil
}

func FuncC(sc string, vv int) error {
	b := &VB{B: 3}
	errB := b.CheckB(vv)
	if errB != nil {
		return errB
	}

	// 更多逻辑...

	return nil
}

我们在函数FuncA中进行了CheckACheckB,在函数FuncB中进行了CheckA。然而,在函数FuncC中只进行了CheckB。有一个问题是,当CheckA的返回值发生变化时,FuncAFuncB都需要进行修改。

我们想要重构上述代码。在Golang中是否有一种优雅的方式来实现这一点?

我们尝试将CheckACheckB合并到一个名为ValidateFunc的函数中,如下所示:

type VALIDATE int

const (
	_ VALIDATE = 1 << iota
	VALIDATEA
	VALIDATEB
)

func ValidateFunc(vs, s string, vi, i int, validate VALIDATE) error {
	if validate&VALIDATEA == VALIDATEA {
		a := &VA{A: vs}
		errA := a.CheckA(s)
		if errA != nil {
			return errA
		}
	}
	if validate&VALIDATEB == VALIDATEB {
		b := &VB{B: vi}
		errB := b.CheckB(i)
		if errB != nil {
			return errB
		}
	}
	return nil
}

func FuncA(s string, i int) error {
	err := ValidateFunc("testa", s, 3, i, VALIDATEA|VALIDATEB)
	if err != nil {
		return err
	}

	// 更多逻辑...

	return nil
}
英文:

There are multiple condition checks in multiple functions

type VA struct {
	A string
}

func (va *VA) CheckA(s string) error {
	if s != va.A {
		return errors.New(&quot;invalid str &quot;)
	}
	return nil
}

type VB struct {
	B int
}

func (vb *VB) CheckB(i int) error {
	if i == vb.B {
		return errors.New(&quot;invalid int&quot;)
	}
	return nil
}

func FuncA(s string, i int) error {
	a := &amp;VA{A: &quot;testa&quot;}
	errA := a.CheckA(s)
	if errA != nil {
		return errA
	}

	b := &amp;VB{B: 3}
	errB := b.CheckB(i)
	if errB != nil {
		return errB
	}

	// more logic ...

	return nil
}

func FuncB(sb string, v int32) error {
	a := &amp;VA{A: &quot;testb&quot;}
	errA := a.CheckA(sb)
	if errA != nil {
		return errA
	}

	// more logic ...

	return nil
}

func FuncC(sc string, vv int) error {
	b := &amp;VB{B: 3}
	errB := b.CheckB(vv)
	if errB != nil {
		return errB
	}

	// more logic ...

	return nil
}

We do CheckA and CheckB in function FuncA and do CheckA in function FuncB. However, only do CheckB in function FuncC. There is one pitfall that when the return value of CheckA is changed, both FuncA and FuncB would be changed.

We want to refactor the above codes. Is there any elegant way to do that in Golang?

What we have tried, combine CheckA and CheckB in one function ValidateFunc like below

type VALIDATE int

const (
	_ VALIDATE = 1 &lt;&lt; iota
	VALIDATEA
	VALIDATEB
)

func ValidateFunc(vs, s string, vi, i int, validate VALIDATE) error {
	if validate&amp;VALIDATEA == VALIDATEA {
		a := &amp;VA{A: vs}
		errA := a.CheckA(s)
		if errA != nil {
			return errA
		}
	}
	if validate&amp;VALIDATEB == VALIDATEB {
		b := &amp;VB{B: vi}
		errB := b.CheckB(i)
		if errB != nil {
			return errB
		}
	}
	return nil
}

func FuncA(s string, i int) error {
	err := ValidateFunc(&quot;testa&quot;, s, 3, i, VALIDATEA|VALIDATEB)
	if err != nil {
		return err
	}

	// more logic ...

	return nil
}

答案1

得分: 1

参考选项模式,代码似乎比以前更简单。

type Validator struct {
	A VA
	B VB
}

type Validate func(v *Validator) error

func WithVA(s string) Validate {
	return func(v *Validator) error {
		if err := v.A.CheckA(s); err != nil {
			return err
		}
		return nil
	}
}

func WithVB(i int) Validate {
	return func(v *Validator) error {
		if err := v.B.CheckB(i); err != nil {
			return err
		}
		return nil
	}
}

func DoValidate(vs string, vi int, vals ...func(v *Validator) error) error {
	v := &Validator{A: VA{A: vs}, B: VB{B: vi}}

	for _, val := range vals {
		if err := val(v); err != nil {
			return err
		}
	}

	return nil
}

func FuncA(s string, i int) error {
	err := DoValidate("testa", 3, WithVA(s), WithVB(i))
	if err != nil {
		return err
	}

	// more logic ...

	return nil
}
英文:

Refer to Option pattern, it seems the codes will be simpler than before.

type Validator struct {
	A VA
	B VB
}

type Validate func(v *Validator) error

func WithVA(s string) Validate {
	return func(v *Validator) error {
		if err := v.A.CheckA(s); err != nil {
			return err
		}
		return nil
	}
}

func WithVB(i int) Validate {
	return func(v *Validator) error {
		if err := v.B.CheckB(i); err != nil {
			return err
		}
		return nil
	}
}

func DoValidate(vs string, vi int, vals ...func(v *Validator) error) error {
	v := &amp;Validator{A: VA{A: vs}, B: VB{B: vi}}

	for _, val := range vals {
		if err := val(v); err != nil {
			return err
		}
	}

	return nil
}

func FuncA(s string, i int) error {
	err := DoValidate(&quot;testa&quot;, 3, WithVA(s), WithVB(i))
	if err != nil {
		return err
	}

	// more logic ...

	return nil
}

答案2

得分: -1

也许将VA和VB合并成一个新的结构体,然后验证它?

英文:

Perhaps combine VA & VB into a new struct
Then validate that one?

huangapple
  • 本文由 发表于 2022年1月25日 16:54:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/70845815.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定