함수 호출 시에 모든 parameter를 기본적으로 입력해야한다. 하지만 경우에 따라 전체의 Parameter가 아닌 특정 parameter만을 함수를 만들고 싶은 경우도 자주 발생한다. 이러한 경우에 Optin Funtions Pattern을 활용하여 Parameter를 optional 받는 방법에 대해 공부해 보았습니다.
아래의 코드에서는 Concat 함수는 필수적으로 3개의 paramter를 받고 있습니다. 이 중에 하나라도 빠지면 err가 발생할 것 입니다.
func Concat(strs []string, separator string, brackets [2]string) string {
concat := strings.Join(strs, separator)
concat = brackets[0] + concat + brackets[1]
return concat
}
func TestConcatenate(t *testing.T) {
strs := []string{
"hell", "world", "good", "mooning",
}
concat := Concat(strs, "-", [2]string{"<", ">"})
t.Log(concat)
}
위의 경우 항상 separator, brackets 과 같은 옵션을 넣어줘야 한다. 만약 이를 default separator, bracket을 설정하고, optional하게 separator과 bracket을 받기를 원하는 경우, interface을 활용하여 func Concat(strs []string, ...opts interface{})로 사용하는 방법도 있겟지만 이는 opts를 casting시에 err를 야기하기 쉽기에 function의 사용성이 많이 떨어진다.
Otpin Funtions Pattern을 활용시에는 아래의 코드와 같이 필요시에만 option을 넣어서 Concat2함수를 호출한다.
아래에서는 SetSeparatorOption, SetBracketsOption 함수를 만들어서 각각에 맞는 인자를 set하고 있다. 그리고 Concat함수는 opts로 Option이라는 function을 parameter로 가진다.
type options struct {
separator string
brackets [2]string
}
type Option func(*options)
func SetSeparatorOption(separator string) Option {
return func(o *options) {
o.separator = separator
}
}
func SetBracketsOption(brackets [2]string) Option {
return func(o *options) {
o.brackets = brackets
}
}
func Concat2(strs []string, opts ...Option) string {
options := &options{
separator: ",",
brackets: [2]string{"[", "]"},
}
for _, opt := range opts {
opt(options)
}
return Concat(strs, options.separator, options.brackets)
}
func TestConcatenate2(t *testing.T) {
strs := []string{
"hell", "world", "good", "mooning",
}
t.Run("no option", func(t *testing.T) {
concat := Concat2(strs)
t.Log(concat)
})
t.Run("separator option", func(t *testing.T) {
concat := Concat2(strs, SetSeparatorOption("-"))
t.Log(concat)
})
t.Run("bracket option", func(t *testing.T) {
concat := Concat2(strs, SetBracketsOption([2]string{"(", ")"}))
t.Log(concat)
})
t.Run("two option", func(t *testing.T) {
concat := Concat2(strs, SetSeparatorOption("-"), SetBracketsOption([2]string{"(", ")"}))
t.Log(concat)
})
}
optional funtions pattern을 활용하여 mysql connection의 parmeter를 optional하게 생성하는 함수를 생성해 보았다.
const (
Addr = "localhost:4321"
User = "root"
Password = "password"
)
func TestMysqlConnection(t *testing.T) {
ConnectWithMySQL(Addr, User, Password)
ConnectWithMySQL(Addr, User, Password, SetNet("UDT"))
ConnectWithMySQL(Addr, User, Password, SetNet("UDT"), SetLoc(time.Local))
}
type MysqlOptions func(*mysql.Config)
func SetLoc(loc *time.Location) MysqlOptions {
return func(config *mysql.Config) {
config.Loc = loc
}
}
func SetNet(net string) MysqlOptions {
return func(config *mysql.Config) {
config.Net = net
}
}
func ConnectWithMySQL(addr, usr, pwd string, opts ...MysqlOptions) *sql.DB {
cfg := mysql.Config{
User: usr,
Passwd: pwd,
Net: "tcp",
Addr: addr,
Collation: "utf8mb4_general_ci",
Loc: time.UTC,
MaxAllowedPacket: 4 << 20.,
AllowNativePasswords: true,
CheckConnLiveness: true,
DBName: "admin",
}
for _, opt := range opts {
opt(&cfg)
}
connector, err := mysql.NewConnector(&cfg)
if err != nil {
panic(err)
}
db := sql.OpenDB(connector)
return db
}
위 코드에서 ConnectWithMongoDB함수의 parameter 중 addr, usr, pwd와 다르게 default값으로 받는 것들이 유리한 것들이 있다. 여기서는 loc과 net이다. 이렇게 우선 parmeter로 option funtion를 인자로 받게 하고, 추후에 ConnectWithMongoDB를 사용하는 곳에서 SetNet, SetLoc과 같은 funtion을 만들어서 사용하게 하는 것 또한 괜찮은 방법이라고 생각한다.(전반적으로 코드가 깔끔해졌습니다.)
잘 보고 갑니다..