缘起

最近复习设计模式
拜读谭勇德的<<设计模式就该这样学>>
本系列笔记拟采用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

  1. package structural_patterns
  2. import (
  3. "learning/gooop/structural_patterns/composite"
  4. "testing"
  5. )
  6. func Test_CompositePattern(t *testing.T) {
  7. user := composite.NewMockUser(1, "张三")
  8. sc := composite.NewSimpleCourse(11, "Java基础", 100)
  9. user.Learn(sc)
  10. user = composite.NewMockUser(2, "李四")
  11. cc := composite.NewCompositeCourse(21, "Java架构师", 500)
  12. cc.Append(composite.NewSimpleCourse(11, "Java基础", 100))
  13. cc.Append(composite.NewSimpleCourse(12, "Java高级编程", 100))
  14. cc.Append(composite.NewSimpleCourse(13, "设计模式", 100))
  15. cc.Append(composite.NewSimpleCourse(14, "Spring技术内幕", 100))
  16. cc.Append(composite.NewSimpleCourse(15, "SpringCloud架构指南", 100))
  17. user.Learn(cc)
  18. }

测试输出

  1. $ go test -v composite_pattern_test.go
  2. === RUN Test_CompositePattern
  3. 张三 is learning Java基础
  4. 李四 is learning Java架构师.Java基础
  5. 李四 is learning Java架构师.Java高级编程
  6. 李四 is learning Java架构师.设计模式
  7. 李四 is learning Java架构师.Spring技术内幕
  8. 李四 is learning Java架构师.SpringCloud架构指南
  9. --- PASS: Test_CompositePattern (0.00s)
  10. PASS
  11. ok command-line-arguments 0.005s

IUser.go

定义学员用户接口

  1. package composite
  2. type IUser interface {
  3. ID() int
  4. Name() string
  5. Learn(course ICourse)
  6. }

ICourse.go

定义课程的公共接口

  1. package composite
  2. type ICourse interface {
  3. ID() int
  4. Name() string
  5. Price() float64
  6. SetUser(user IUser)
  7. Learn() LearningStates
  8. }
  9. type LearningStates int
  10. const MORE LearningStates = 1
  11. const DONE LearningStates = 2

ICompositeCourse.go

组合课程的个性化接口, 从ICourse继承, 并添加了Append(子课程)的方法

  1. package composite
  2. type ICompositeCourse interface {
  3. ICourse
  4. Append(course ICourse)
  5. }

tMockUser.go

虚拟学员的实现类

  1. package composite
  2. type tMockUser struct {
  3. iID int
  4. sName string
  5. }
  6. func NewMockUser(id int, name string) IUser {
  7. return &tMockUser{
  8. id, name,
  9. }
  10. }
  11. func (me *tMockUser) ID() int {
  12. return me.iID
  13. }
  14. func (me *tMockUser) Name() string {
  15. return me.sName
  16. }
  17. func (me *tMockUser) Learn(course ICourse) {
  18. course.SetUser(me)
  19. for {
  20. state := course.Learn()
  21. if state == DONE {
  22. break
  23. }
  24. }
  25. }

tSimpleCourse.go

简单课程的实现类, 实现ICourse接口

  1. package composite
  2. import "fmt"
  3. type tSimpleCourse struct {
  4. iID int
  5. sName string
  6. fPrice float64
  7. mUser IUser
  8. }
  9. func NewSimpleCourse(id int, name string, price float64) ICourse {
  10. return &tSimpleCourse{
  11. id, name, price, nil,
  12. }
  13. }
  14. func (me *tSimpleCourse) ID() int {
  15. return me.iID
  16. }
  17. func (me *tSimpleCourse) Name() string {
  18. return me.sName
  19. }
  20. func (me *tSimpleCourse) Price() float64 {
  21. return me.fPrice
  22. }
  23. func (me *tSimpleCourse) SetUser(user IUser) {
  24. me.mUser = user
  25. }
  26. func (me *tSimpleCourse) Learn() LearningStates {
  27. fmt.Printf("%s is learning %s\n", me.mUser.Name(), me.sName)
  28. return DONE
  29. }

tCompositeCourse.go

组合课程的实现类, 继承tSimpleCourse, 并实现了ICompositeCourse接口

  1. package composite
  2. import "fmt"
  3. type tCompositeCourse struct {
  4. tSimpleCourse
  5. mCourseList []ICourse
  6. iCourseIndex int
  7. }
  8. func NewCompositeCourse(id int, name string, price float64) ICompositeCourse {
  9. return &tCompositeCourse {
  10. tSimpleCourse: tSimpleCourse{
  11. id, name, price, nil,
  12. },
  13. mCourseList: make([]ICourse, 0),
  14. iCourseIndex: 0,
  15. }
  16. }
  17. func (me *tCompositeCourse) Append(course ICourse) {
  18. me.mCourseList = append(me.mCourseList, course)
  19. }
  20. func (me *tCompositeCourse) Learn() LearningStates {
  21. if me.IsDone() {
  22. fmt.Printf("%s is learning %s: no more courses\n", me.mUser.Name(), me.Name())
  23. return DONE
  24. }
  25. course := me.mCourseList[me.iCourseIndex]
  26. fmt.Printf("%s is learning %s.%s\n", me.mUser.Name(), me.Name(), course.Name())
  27. me.iCourseIndex++
  28. if me.IsDone() {
  29. return DONE
  30. } else {
  31. return MORE
  32. }
  33. }
  34. func (me *tCompositeCourse) IsDone() bool {
  35. return me.iCourseIndex >= len(me.mCourseList)
  36. }

组合模式小结

组合模式的优点
(1)清楚地定义各层次的复杂对象,表示对象的全部或部分层次。
(2)让客户端忽略了层次的差异,方便对整个层次结构进行控制。
(3)简化客户端代码。
(4)符合开闭原则。
组合模式的缺点
(1)限制类型时会较为复杂。
(2)使设计变得更加抽象。

(end)