I have a struct in Go which has more than ten methods. And all of them are almost the same. In fact, the difference is only in one line.
Let's see the simplified example. Here is the first method:
func (s *service) GetUser(i int, s string) (*SomeObject, error) {
CommonMethod1()
CommonMethod2()
user, err := s.internalLayer.GetUser(i, s)
if err != nil {
CommonMethod3()
}
return user, err
}
And the second one:
func (s *service) CheckUser(i int) (bool, error) {
CommonMethod1()
CommonMethod2()
exists, err := s.internalLayer.CheckUser(i)
if err != nil {
CommonMethod3()
}
return exists, err
}
And there are almost 10 others with a lot of copy and paste. I'd like to ameliorate this code and the idea is to create some common method that I'll call everywhere.
It should
CommonMethod1()
CommonMethod2()
CommonMethod3()
in case of errorCan you tell me if it's possible to implement in Go such common method?
user, err := s.GetUser(i, s)
exists, err := s.CheckUser(i)
Has different parameters count and different return types.
You can DRY it using closures:
func GetUser2(i int, s string) (*User, error) {
var u *User
err := do(func() error {
var err error
u, err = getUser(i, s)
return err
})
return u, err
}
func CheckUser2(i int) (bool, error) {
var b bool
err := do(func() error {
var err error
b, err = checkUser(i)
return err
})
return b, err
}
func do(f func() error) error {
CommonMethod1()
CommonMethod2()
err := f()
if err != nil {
CommonMethod3()
}
return err
}
If CommonMethod1()
and CommonMethod2()
calls do not have to precede the calls to the internal layer, basically they can be incorporated into a check function, and you don't even need closures or function values (there's also no need to return the passed error from check()
):
func (s *service) GetUser(i int, s string) (*SomeObject, error) {
user, err := s.internalLayer.GetUser(i, s)
check(err)
return user, err
}
func (s *service) CheckUser(i int) (bool, error) {
exists, err := s.internalLayer.CheckUser(i)
check(err)
return exists, err
}
func check(err error) {
CommonMethod1()
CommonMethod2()
if err != nil {
CommonMethod3()
}
}
If CommonMethod1()
and CommonMethod2()
calls must precede the calls to the internal layer, then you may use closures, but using named result parameters simplifies the code, and again, it's unnecessary to return the error from the common function:
func (s *service) GetUser(i int, s string) (user *SomeObject, err error) {
do(func() error {
user, err = s.internalLayer.GetUser(i, s)
return err
})
return
}
func (s *service) CheckUser(i int) (exists bool, err error) {
do(func() error {
exists, err = s.internalLayer.CheckUser(i)
return err
})
return
}
func do(f func() error) {
CommonMethod1()
CommonMethod2()
if err := f(); err != nil {
CommonMethod3()
}
}