聚焦目标
理解kubectl的核心实现之一:Visitor Design Pattern 访问者模式
目录
什么是访问者模式
 
kubectl中的Visitor
 
Visitor的链式处理
- 多个对象聚合为一个对象
- VisitorList
 
- EagerVisitorList 
 
 
- 多个方法聚合为一个方法
- DecoratedVisitor
 
- ContinueOnErrorVisitor
 
 
- 将对象抽象为多个底层对象,逐个调用方法
- FlattenListVisitor
 
- FilteredVisitor
 
 
 
Visitor的各类实现
- StreamVisitor
 
- FileVisitor
 
- URLVisitor
 
- KustomizeVisitor
 
 
visitor design pattern
在设计模式中,访问者模式的定义为:
允许一个或者多个操作应用到对象上,解耦操作和对象本身
那么,对一个程序来说,具体的表现就是:
- 表面:某个对象执行了一个方法
 
- 内部:对象内部调用了多个方法,最后统一返回结果
 
举个例子,
- 表面:调用一个查询订单的接口
 
- 内部:先从
缓存中查询,没查到再去热点数据库查询,还没查到则去归档数据库里查询 
Visitor
我们来看看kubeadm中的访问者模式的定义:
1 2 3 4 5 6
   |  type Visitor interface { 	Visit(VisitorFunc) error }
  type VisitorFunc func(*Info, error) error
 
  | 
 
基本的数据结构很简单,但从当前的数据结构来看,有两个问题:
单个操作 可以直接调用Visit方法,那多个操作如何实现呢? 
- 在应用
多个操作时,如果出现了error,该退出还是继续应用下一个操作呢? 
Chained
VisitorList
封装多个Visitor为一个,出现错误就立刻中止并返回
1 2 3 4 5 6 7 8 9 10 11 12
   |  type VisitorList []Visitor
 
  func (l VisitorList) Visit(fn VisitorFunc) error { 	for i := range l { 		if err := l[i].Visit(fn); err != nil { 			return err 		} 	} 	return nil }
 
  | 
 
EagerVisitorList
封装多个Visitor为一个,出现错误暂存下来,全部遍历完再聚合所有的错误并返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   |  type EagerVisitorList []Visitor
 
  func (l EagerVisitorList) Visit(fn VisitorFunc) error { 	errs := []error(nil) 	for i := range l { 		if err := l[i].Visit(func(info *Info, err error) error { 			if err != nil { 				errs = append(errs, err) 				return nil 			} 			if err := fn(info, nil); err != nil { 				errs = append(errs, err) 			} 			return nil 		}); err != nil { 			errs = append(errs, err) 		} 	} 	return utilerrors.NewAggregate(errs) }
 
  | 
 
DecoratedVisitor
这里借鉴了装饰器的设计模式,将一个Visitor调用多个VisitorFunc方法,封装为调用一个VisitorFunc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   |  type DecoratedVisitor struct { 	visitor    Visitor 	decorators []VisitorFunc }
 
  func (v DecoratedVisitor) Visit(fn VisitorFunc) error { 	return v.visitor.Visit(func(info *Info, err error) error { 		if err != nil { 			return err 		} 		for i := range v.decorators { 			if err := v.decorators[i](info, nil); err != nil { 				return err 			} 		} 		return fn(info, nil) 	}) }
 
  | 
 
ContinueOnErrorVisitor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
   |  type ContinueOnErrorVisitor struct { 	Visitor }
 
  func (v ContinueOnErrorVisitor) Visit(fn VisitorFunc) error { 	errs := []error{} 	err := v.Visitor.Visit(func(info *Info, err error) error { 		if err != nil { 			errs = append(errs, err) 			return nil 		} 		if err := fn(info, nil); err != nil { 			errs = append(errs, err) 		} 		return nil 	}) 	if err != nil { 		errs = append(errs, err) 	} 	if len(errs) == 1 { 		return errs[0] 	} 	return utilerrors.NewAggregate(errs) }
 
  | 
 
FlattenListVisitor
将runtime.ObjectTyper解析成多个runtime.Object,再转换为多个Info,逐个调用VisitorFunc
1 2 3 4 5
   | type FlattenListVisitor struct { 	visitor Visitor 	typer   runtime.ObjectTyper 	mapper  *mapper }
  | 
 
FilteredVisitor
对Info资源的检验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   |  type FilteredVisitor struct { 	visitor Visitor 	filters []FilterFunc }
  func (v FilteredVisitor) Visit(fn VisitorFunc) error { 	return v.visitor.Visit(func(info *Info, err error) error { 		if err != nil { 			return err 		} 		for _, filter := range v.filters {        			ok, err := filter(info, nil) 			if err != nil { 				return err 			} 			if !ok { 				return nil 			} 		} 		return fn(info, nil) 	}) }
 
  | 
 
Implements
StreamVisitor
最基础的Visitor
1 2 3 4 5 6 7 8
   | type StreamVisitor struct {    	io.Reader 	*mapper
  	Source string 	Schema ContentValidator }
  | 
 
FileVisitor
文件的访问,包括标准输入,底层调用StreamVisitor来访问
1 2 3 4 5
   | type FileVisitor struct {    	Path string 	*StreamVisitor }
  | 
 
URLVisitor
HTTP用GET方法获取数据,底层也是复用StreamVisitor
1 2 3 4 5 6
   | type URLVisitor struct { 	URL *url.URL 	*StreamVisitor    	HttpAttemptCount int }
  | 
 
KustomizeVisitor
自定义的Visitor,针对自定义的文件系统
1 2 3 4
   | type KustomizeVisitor struct { 	Path string 	*StreamVisitor }
  | 
 
Github: https://github.com/Junedayday/code_reading
Blog: http://junes.tech/
Bilibili:https://space.bilibili.com/293775192
公众号:golangcoding