5

手撸golang 结构型设计模式 组合模式

 3 years ago
source link: https://segmentfault.com/a/1190000039162976
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

手撸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)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK