设计模式的提出已经有很长时间了,他可以帮助我们更好的组织代码和结构,也许有的人对此不屑一顾,认为代码是基于演化的而不应该囿于模式,但实际上随着代码规模的增大,如果我们还追求良好的维护性、扩展性,那设计模式是不可或缺的,今天就来介绍一种和变更有关的设计模式。
假设你维护一个人员管理系统,里面有各种类型人员,比如总经理、行政、商务。假设其他团队要求你提供一个获取人员考勤的实现,那么你该如何实现?
第一,将 getDays 方法添加到接口,然后每个类型都去实现,这似乎是比较好的解决方法,但是代价也比较高,你需要改动每一个类型的实现,你肯定不希望只有有需求就改动自己的宝贝代码吧
第二,让需求方自己实现,但是这并不总是可行的
第三,就是使用访问者模式实现
首先定义一个如下访问者接口:
type visitor interface {
visitForManage(manage)
visitForAdmin(admin)
visitForMarket(market)
}
我们可以使用上面接口中的方法来为总经理、行政、商务添加相应的功能。
你可能在想, 为什么我们不再访问者接口里面使用单一的 visit(people)方法呢? 这是因为 Go 语言不支持方法重载, 所以你无法以相同名称、 不同参数的方式来使用方法。
好了, 第二项重要的工作是将 accept接受方法添加至人员接口中。
func accept(v visitor)
所有形状结构体都需要定义此方法, 类似于:
func (obj * manage) accept(v visitor){
v.visitForManage(obj)
}
但是我们不是不希望修改现有的实现吗? 确实是这样,但是在使用访问者模式时, 我们必须要修改结构体。 但这样的修改只需要进行一次。
如果后续添加任何其他行为, 我们将使用相同的 accept(v visitor)函数, 而无需对结构体进行进一步的修改。我们只需简单地定义访问者接口的具体实现即可。
接下来就看一下具体的代码实现吧
people.go: 元件
package main
type People interface {
getType() string
accept(Visitor)
}
mamage.go: 具体元件
package main
type Manage struct {
name string
}
func (s *Manage) accept(v Visitor) {
v.visitForManage(s)
}
func (s *Manage) getType() string {
return "Manage"
}
admin.go: 具体元件
package main
type Admin struct {
name string
}
func (c *Admin) accept(v Visitor) {
v.visitForAdmin(c)
}
func (c *Admin) getType() string {
return "Admin"
}
market.go: 具体元件
package main
type Market struct {
name string
}
func (t *Market) accept(v Visitor) {
v.visitForMarket(t)
}
func (t *Market) getType() string {
return "Market"
}
visitor.go: 访问者
package main
type Visitor interface {
visitForManage(*Manage)
visitForAdmin(*Admin)
visitForMarket(*Market)
}
daysCalculator.go: 具体访问者
package main
import (
"fmt"
)
type DaysCalculator struct {
days int
}
func (a *DaysCalculator) visitForManage(*Manage) {
fmt.Println("Calculating visitForManage")
}
func (a *DaysCalculator) visitForAdmin(*Admin) {
fmt.Println("Calculating visitForAdmin")
}
func (a *DaysCalculator) visitForMarket(*Market) {
fmt.Println("Calculating visitForMarket")
}