tips:对结构体中的字段,会频繁变化、需要灵活配置、有默认值的场景,可能会用到 Option 模式。
往简单来说,有下面这一个需求:
如果创造了一个英雄,我想修改它的属性,怎么才能方便的去修改,包括之后新增的字段,如果不直接用结构体.字段
的形式,保证了修改英雄属性的安全性,
2021/12/30 09:30:52 英雄创造后属性为: &{1001 Default 1 1 map[]}
2021/12/30 09:30:52 英雄魔改后属性为: &{1001 libai 3 10 map[name:libai]}
Option 模式
Option 模式的专业术语为:Functional Options Pattern(函数式选项模式)函数的参数传递是一个 Options。
为开发者提供了将一个函数的参数设置为可选的功能,每次新增选项时,可以不改变接口保持兼容,并且参数无顺序要求。
package main
import "log"
type Hero struct {
HeroId int
Name string // 名字
SkillId int // 技能Id
Speed int // 移动速度
Extra map[string]string
}
type Option struct {
Change func(hero *Hero)
}
func NewHeroTemplate() *Hero {
return &Hero{
HeroId: 1001,
Name: "Default",
SkillId: 1,
Speed: 1,
Extra: make(map[string]string),
}
}
// 改变英雄名字
func WithName(name string) *Option {
return &Option{Change: func(hero *Hero) {
hero.Name = name
}}
}
// 改变技能Id
func WithSkillId(skillId int) *Option {
return &Option{Change: func(hero *Hero) {
hero.SkillId = skillId
}}
}
// 改变速度
func WithSpeed(speed int) *Option {
return &Option{Change: func(hero *Hero) {
hero.Speed = speed
}}
}
// 增加扩展信息
func WithExtra(key string, value string) *Option {
return &Option{Change: func(hero *Hero) {
hero.Extra[key] = value
}}
}
func GetHero(options ...*Option) *Hero {
hero := NewHeroTemplate()
for _, opt := range options {
opt.Change(hero)
}
return hero
}
func main() {
hero:=GetHero(
WithName("libai"),
WithSkillId(2),
WithSkillId(3),
WithSpeed(2),
WithSpeed(10),
WithExtra("name", "libai"))
log.Println("英雄创造后属性为:",hero)
}
此时如果我们要新加一个字段,只需要对应增加一个WithXXX的方法,在原来的调用代码处以新增代码的方式调用,以很小的成本去新增一个扩展!同理,这个编写代码的方式也可以作用在配置类上,新增配置,或者Server服务器新增参数,新增服务等等
缺点:就是字段多的话,对应的方法也多,但注意了使用场景避免缺点去使用才是一名优秀的攻城狮!