设计模式的出现,是为了解决编程中遇到的一些问题,现在有一个场景:
我们需要初始化一个服务的配置文件,比如是这样:
type Config struct {
id int
name string
ip string
port string
// other params ...
}
常见的初始化,有以下几种:
方式一:
func initConfig(id int, name, ip, port string) {
conf.id = id
conf.name = name
conf.ip = ip
conf.port = port
}
这种方式,参数比较固定,比如我以后想扩展一些参数是很吃力的,而且,参数的位置也比较固定,就是说这个函数一旦开始被使用,参数位置就不能随意更改了,非常不灵活。
注意,可变参数(params ...interface{})也是一样,不能解决根本问题,参数位置还是固定的,而且函数内部还需要断言处理。
有些同学说,那我传个结构体不就行了吗,就不用考虑扩展参数和参数顺序问题了,于是就有了下一种方式:
方式二:
func initConfig(c Config) {
conf.id = c.id
conf.name = c.name
conf.ip = c.ip
conf.port = c.port
}
这种方式确实解决了以上两个问题,但是也把所有配置全部暴露出去了,不但破坏了程序的封装性,而且,如果我想要一些默认的初始化,比如端口号默认8000,外部支持缺省配置,这又成问题了,于是又有了另一种方式:
方式三:
func setId(id int) {
conf.id = id
}
func setName(name string) {
conf.name = name
}
...
这种方式确实解决了以上问题,但是也产生了新的问题,随着参数的扩展,程序中的setXXX(或initXXX)方法也逐渐增加,而且,参数设置被分的很零散,不便于程序管理,再比如这时候想做个初始化统计,这种也是很繁琐,等等诸多问题。
=================
综上,那有没有一种方式,能够支持缺省配置,也能够在参数上随意的更换位置,初始化配置也不要调那么分散,能解决以上的这些问题呢?
于是,就有了option模式:
我这里先提供一个定义示例:
type Option func(opt *options)
type options struct {
id int
name string
ip string
port string
// other params ...
}
func WithId(id int) Option {
return func(opt *options) {
opt.id = id
}
}
func WithName(name string) Option {
return func(opt *options) {
opt.name = name
}
}
然后是设置:
var conf = &options{
port: "8000",
}
func Parse(opt ...Option) {
for _, o := range opt {
o(conf)
}
}
这就达到想要的效果了,需要扩展参数的时候,如果想对外暴露,就添加对应的With方法就可以了。
最后看一下使用:
func main() {
Parse(WithName("testName"), WithId(1000))
}
使用也很简洁,每一个参数都是Option类型,参数数量和顺序都可以随意控制,达到了想要的效果!