手撸golang 结构型设计模式 组合模式
2011 年 7 月 10 日
手撸golang 结构型设计模式 组合模式
缘起
最近复习设计模式
拜读谭勇德的<>
本系列笔记拟采用golang练习之
组合模式
组合模式(Composite Pattern)又叫作整体-部分(Part-Whole)模式,它的宗旨是通过将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得客户对单个对象和组合对象的使用具有一致性,属于结构型设计模式。
_
透明组合vs安全组合
- 透明组合: 叶子节点和树枝节点具有完全一致的接口
- 安全组合: 叶子节点和树枝节点从公共接口继承, 并做个性化扩展
场景
- 某线上学校, 提供系列java学习课程
- 提供某些简单课程, 如”java基础”, 学员可一次性学完
- 提供某些组合课程, 内部包含了若干简单课程, 如”java架构师”课程, 内部包含”java基础”, “java高级编程”等多门课程, 需要分步依次学习, 才能完成
-
为方便业务层调用, 根据 安全组合模式
, 简单课程和组合课程均实现公共接口的Learn方法.
设计
- IUser: 定义学员用户接口
- ICourse: 定义课程的公共接口
- ICompositeCourse: 组合课程的个性化接口, 从ICourse继承, 并添加了Append(子课程)的方法
- tMockUser: 虚拟学员的实现类
- tSimpleCourse: 简单课程的实现类, 实现ICourse接口
- tCompositeCourse: 组合课程的实现类, 继承tSimpleCourse, 并实现了ICompositeCourse接口
单元测试
composite_pattern_test.go
package structural_patterns import ( "learning/gooop/structural_patterns/composite" "testing" ) func Test_CompositePattern(t *testing.T) { user := composite.NewMockUser(1, "张三") sc := composite.NewSimpleCourse(11, "Java基础", 100) user.Learn(sc) user = composite.NewMockUser(2, "李四") cc := composite.NewCompositeCourse(21, "Java架构师", 500) cc.Append(composite.NewSimpleCourse(11, "Java基础", 100)) cc.Append(composite.NewSimpleCourse(12, "Java高级编程", 100)) cc.Append(composite.NewSimpleCourse(13, "设计模式", 100)) cc.Append(composite.NewSimpleCourse(14, "Spring技术内幕", 100)) cc.Append(composite.NewSimpleCourse(15, "SpringCloud架构指南", 100)) user.Learn(cc) }
测试输出
$ go test -v composite_pattern_test.go === RUN Test_CompositePattern 张三 is learning Java基础 李四 is learning Java架构师.Java基础 李四 is learning Java架构师.Java高级编程 李四 is learning Java架构师.设计模式 李四 is learning Java架构师.Spring技术内幕 李四 is learning Java架构师.SpringCloud架构指南 --- PASS: Test_CompositePattern (0.00s) PASS ok command-line-arguments 0.005s
IUser.go
定义学员用户接口
package composite type IUser interface { ID() int Name() string Learn(course ICourse) }
ICourse.go
定义课程的公共接口
package composite type ICourse interface { ID() int Name() string Price() float64 SetUser(user IUser) Learn() LearningStates } type LearningStates int const MORE LearningStates = 1 const DONE LearningStates = 2
ICompositeCourse.go
组合课程的个性化接口, 从ICourse继承, 并添加了Append(子课程)的方法
package composite type ICompositeCourse interface { ICourse Append(course ICourse) }
tMockUser.go
虚拟学员的实现类
package composite type tMockUser struct { iID int sName string } func NewMockUser(id int, name string) IUser { return &tMockUser{ id, name, } } func (me *tMockUser) ID() int { return me.iID } func (me *tMockUser) Name() string { return me.sName } func (me *tMockUser) Learn(course ICourse) { course.SetUser(me) for { state := course.Learn() if state == DONE { break } } }
tSimpleCourse.go
简单课程的实现类, 实现ICourse接口
package composite import "fmt" type tSimpleCourse struct { iID int sName string fPrice float64 mUser IUser } func NewSimpleCourse(id int, name string, price float64) ICourse { return &tSimpleCourse{ id, name, price, nil, } } func (me *tSimpleCourse) ID() int { return me.iID } func (me *tSimpleCourse) Name() string { return me.sName } func (me *tSimpleCourse) Price() float64 { return me.fPrice } func (me *tSimpleCourse) SetUser(user IUser) { me.mUser = user } func (me *tSimpleCourse) Learn() LearningStates { fmt.Printf("%s is learning %s\n", me.mUser.Name(), me.sName) return DONE }
tCompositeCourse.go
组合课程的实现类, 继承tSimpleCourse, 并实现了ICompositeCourse接口
package composite import "fmt" type tCompositeCourse struct { tSimpleCourse mCourseList []ICourse iCourseIndex int } func NewCompositeCourse(id int, name string, price float64) ICompositeCourse { return &tCompositeCourse { tSimpleCourse: tSimpleCourse{ id, name, price, nil, }, mCourseList: make([]ICourse, 0), iCourseIndex: 0, } } func (me *tCompositeCourse) Append(course ICourse) { me.mCourseList = append(me.mCourseList, course) } func (me *tCompositeCourse) Learn() LearningStates { if me.IsDone() { fmt.Printf("%s is learning %s: no more courses\n", me.mUser.Name(), me.Name()) return DONE } course := me.mCourseList[me.iCourseIndex] fmt.Printf("%s is learning %s.%s\n", me.mUser.Name(), me.Name(), course.Name()) me.iCourseIndex++ if me.IsDone() { return DONE } else { return MORE } } func (me *tCompositeCourse) IsDone() bool { return me.iCourseIndex >= len(me.mCourseList) }
组合模式小结
组合模式的优点
(1)清楚地定义各层次的复杂对象,表示对象的全部或部分层次。
(2)让客户端忽略了层次的差异,方便对整个层次结构进行控制。
(3)简化客户端代码。
(4)符合开闭原则。
组合模式的缺点
(1)限制类型时会较为复杂。
(2)使设计变得更加抽象。
(end)