行爲型模式---golang實現

若是您以爲本篇文章不錯,請記獲得個人GitHub點上一個star,您的支持是我最大的動力!十分感謝!java

命令模式

定義

命令模式( Command Pattern)又稱爲行動( Action)模式或交易( Transaction)模式。git

命令模式的英文定義是:github

Encapsulate a request as an object, thereby letting you parameterize clients withdifferent requests, queue or log requests, and support undoable operations.

意思是:將一個請求封裝成一個對象,從而讓你使用不一樣的請求把客戶端參數化,對請求排隊或,者記錄請求日誌,能夠提供命令的撤銷和恢復功能。golang

簡單來講,命令模式就是將發送者、接收者和調用命令封裝成對象,客戶端調用的時候能夠選擇不一樣的對象,從而實現發送者和接收者的徹底解耦。算法

類圖

命令模式包含以下角色:sql

●命令接口(Command)角色:該角色聲明一一個接口,定義須要執行的命令;docker

●具體命令實現類( Concrete Command) 角色:該角色定義一個接收者和行爲之間的弱耦合,實現命令方法,並調用接收者的相應操做;數據庫

●調用者(Invoker) 角色:該角色負責調用命令對象執行請求;編程

●接收者( Receiver) 角色:該角色負責具體實施和執行請求動做(方法) ;設計模式

●客戶端(Client)角色:串連執行整個流程。

優缺點

命令模式的優勢:

  • 類間解耦:調用者角色與接收者角色之間沒有任何依賴關係,調用者實現功能時只須要調用Command中的execute()方法便可,不須要了解是哪一個接收者執行;
  • 可擴展性: Command的子類能夠很是容易地擴展,而調用者Invoker和高層次的模塊Client不產生嚴重的代碼耦合。

命令模式的缺點:

  • 使用命令模式會致使系統有過多的具體命令類,由於針對每一一個命令都須要設計一一個具體命令類。

應用場景

命令模式的典型應用場景以下:

  • 系統須要支持命令的撤銷(undo),命令對象能夠把狀態存儲起來,等到客戶端須要撤銷時,能夠調用undo() 方法,將命令所產生的效果撤銷;
  • 系統須要支持命令的撤銷(Undo )操做和恢復( Redo)操做;
  • 系統須要將一-組操做組合在- 起,使用命令模式來實現, 能夠很方便的增長新的命令。

使用實例:遙控器

import "fmt"
// 命令接口和實現類
type command interface {
    Execute()
}

type OpenTvCommand struct {
    tv *TV
}

func (o *OpenTvCommand) Execute()  {
    o.tv.Open()
}

type CloseTvCommand struct {
    tv *TV
}

func (c *CloseTvCommand) Execute()  {
    c.tv.Close()
}

type ChangeTvCommand struct {
    tv *TV
}

func (c *ChangeTvCommand) Execute()  {
    c.tv.Change()
}
// 命令的接收者
type TV struct {}

func (tv *TV) Open()  {
    fmt.Println("打開電視")
}
func (tv *TV) Close()  {
    fmt.Println("關閉電視")
}
func (tv *TV)Change()  {
    fmt.Println("換臺")
}

// 命令的執行者:向TV發起命令
type TVRemote struct {
    open *OpenTvCommand
    change *ChangeTvCommand
    close *CloseTvCommand
}

func (tv *TVRemote) Open ()  {
    tv.open.Execute()
}
func (tv *TVRemote) Change()  {
    tv.change.Execute()
}
func (tv *TVRemote) Close ()  {
    tv.close.Execute()
}
// 建立接收者
    rece := &TV{}
    // 建立命令對象
    openComm := &OpenTvCommand{rece}
    changeComm := &ChangeTvCommand{rece}
    closeComm := &CloseTvCommand{rece}

    // 建立請求者,把命令對象設置進去
    tvR := &TVRemote{
        open:   openComm,
        change: changeComm,
        close:  closeComm,
    }
    tvR.Open()
    tvR.Change()
    tvR.Close()
打開電視
換臺
關閉電視

中介者模式

定義

中介者?其實生活中你們再熟悉不過了這個詞,咱們熟悉的黃牛、房產中介等就是充當中介的角色,將咱們的買票、購房等的需求自身消化再代爲辦理。又好比說中間件,馬老師很忙,不能來,一我的有事就直接找馬老師對吧,因此要找一箇中介,客戶來了直接找中間人,中間人再和馬老師溝通,這樣馬老師和客戶那邊就是一個不可見的關係,由中介者角色進行中間協調,馬老師也能抽出更多時間去忙別的事了,解放了至關的生產力。

中介者模式( Mediator)的定義:定義一箇中介對象來封裝對象之間的交互,使原有對象之間
耦合鬆散,而且能夠獨立地改變它們之間的交互。還記得迪米特法則嗎?迪米特法則的初衷在於下降類之間的耦合,中介者模式就是迪米特法則的典型應用。

類圖

中介者模式的組成角色以下:

  • 中介者(又稱仲裁者,Mediator) :負責定義與Colleague 角色進行通訊和作出角色的接

口;

  • 具體中介者、仲裁者( ConcreteMediator) :負責實現Mediator角色定義的接口,負責具體的業務執行;
  • 同事角色(Colleague) :負責定義與Mediator角色進行通訊的接口;
  • 具體同事角色(ConcreteColleague) :實現Colleague 角色定義的接口,通常會有多個實現類。

優缺點

中介者模式的優勢:

  • 弱化對象間的依賴關係,即鬆耦合,下降同時類的耦合度,符合迪米特法則
  • 將對象間的調用關係進行封裝,使得對象更容易複用

中介者模式的缺點:

  • 若是對象增多,就要去修改抽象中介者和具體的中介者角色
  • 中介者角色承擔了太多了業務邏輯功能,流程複雜時就會顯得比較臃腫,很差管理

應用場景

中介者模式的應用場景通常比較明確,當系統有一系列對象須要相互調用,爲弱化對象間的依賴關係,使得這些對象之間鬆耦合。

使用實例

生活中,最廣泛熟悉的例子就是房屋中介或者qq羣這種聊天案例,這裏咱們以房屋中介爲例,中介公司就比如咱們的中介者角色,而業主和買家就構成了兩個不一樣的同事角色,買賣雙方之間的這種交互就能夠交給中介者去對接協調:

import (
    "fmt"
    "reflect"
)

// 抽象中介公司
type MeditorCompany interface {
    GetSeller() Colleaguer
    SetSeller(seller ColleagueSeller)
    GetBuyer() Colleaguer
    SetBuyer(ColleagueBuyer)
    GetName() string
    SetName(name string)
    Publish(message string,colleaguer Colleaguer)

}
// 具體中介者
type Meditor struct {
    name string
    buyer *ColleagueBuyer
    seller *ColleagueSeller
}

func (m *Meditor) SetSeller(seller ColleagueSeller) {
    m.seller = &seller
}

func (m *Meditor) SetBuyer(b ColleagueBuyer) {
    m.buyer = &b
}

func (m *Meditor) Publish(message string, colleaguer Colleaguer) {
    // 若是是賣家發佈
    if reflect.DeepEqual(colleaguer,m.seller){
        m.buyer.Accept(message)
    } else if reflect.DeepEqual(colleaguer, m.buyer) {
        m.seller.Accept(message)
    }
}

func (m *Meditor) GetSeller() Colleaguer {
    return m.seller
}

func (m *Meditor) GetBuyer() Colleaguer {
    return m.buyer
}

func (m *Meditor) GetName() string {
    return m.name
}

func (m *Meditor) SetName(name string) {
    m.name = name
}

// 抽象同事角色
type Colleaguer interface {
    Colleguer(meditor MeditorCompany)
    Send(string)
    Accept(string)
}

// 賣家-同事角色
type ColleagueSeller struct {
    meditor MeditorCompany
}

func (c *ColleagueSeller) Send(message string) {
    c.meditor.Publish(message,c)
}

func (c *ColleagueSeller) Accept(message string) {
    fmt.Println("賣家收到的消息是"+message)
}

func (c *ColleagueSeller) Colleguer(meditor MeditorCompany) {
    c.meditor = meditor
}

// 買家-同事角色

type ColleagueBuyer struct {
    meditor MeditorCompany
}

func (c *ColleagueBuyer) Colleguer(meditor MeditorCompany) {
    c.meditor = meditor
}

func (c *ColleagueBuyer) Send(message string) {
    c.meditor.Publish(message,c)
}

func (c *ColleagueBuyer) Accept(message string) {
    fmt.Println("買家收到的消息是"+message)
}
var (
        meitdor MeditorCompany
        seller *ColleagueSeller
        buyer *ColleagueBuyer
    )
    seller = &ColleagueSeller{meditor:meitdor}
    buyer = &ColleagueBuyer{meditor:meitdor}
    meitdor = &Meditor{
        name:   "58同城",
        buyer:  buyer,
        seller: seller,
    }
    // 賣家和賣家註冊到中介
    seller.Colleguer(meitdor)
    buyer.Colleguer(meitdor)
    // 發佈需求
    seller.Send("賣一套兩室一廳100平米的Lofty")
    buyer.Send("求購一個兩室一廳的房子")
=== RUN   TestColleagueSeller_Colleguer
買家收到的消息是賣一套兩室一廳100平米的Lofty
賣家收到的消息是求購一個兩室一廳的房子
--- PASS: TestColleagueSeller_Colleguer (0.00s)
PASS

備忘錄模式

模式的定義與特色

備忘錄(Memento)模式的定義:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態,以便之後當須要時能將該對象恢復到原先保存的狀態。該模式又叫快照模式。

備忘錄模式是一種對象行爲型模式,其主要優勢以下。

  • 提供了一種能夠恢復狀態的機制。當用戶須要時可以比較方便地將數據恢復到某個歷史的狀態。
  • 實現了內部狀態的封裝。除了建立它的發起人以外,其餘對象都不可以訪問這些狀態信息。
  • 簡化了發起人類。發起人不須要管理和保存其內部狀態的各個備份,全部狀態信息都保存在備忘錄中,並由管理者進行管理,這符合單一職責原則。

其主要缺點是:資源消耗大。若是要保存的內部狀態信息過多或者特別頻繁,將會佔用比較大的內存資源。

模式的結構與實現

備忘錄模式的核心是設計備忘錄類以及用於管理備忘錄的管理者類,如今咱們來學習其結構與實現。

  1. 模式的結構

備忘錄模式的主要角色以下。

  1. 發起人(Originator)角色:記錄當前時刻的內部狀態信息,提供建立備忘錄和恢復備忘錄數據的功能,實現其餘業務功能,它能夠訪問備忘錄裏的全部信息。
  2. 備忘錄(Memento)角色:負責存儲發起人的內部狀態,在須要的時候提供這些內部狀態給發起人。
  3. 管理者(Caretaker)角色:對備忘錄進行管理,提供保存與獲取備忘錄的功能,但其不能對備忘錄的內容進行訪問與修改。

備忘錄模式的結構圖

模式的應用場景

  1. 須要保存與恢復數據的場景,如玩遊戲時的中間結果的存檔功能。
  2. 須要提供一個可回滾操做的場景,如 Word、記事本、Photoshop,Eclipse 等軟件在編輯時按 Ctrl+Z 組合鍵,還有數據庫中事務操做。
// 備忘錄
type Memento struct {
    state string // 這裏就是保存的狀態
}

func (m *Memento) SetState(s string) {
    m.state = s
}

func (m *Memento) GetState() string {
    return m.state
}
// 發起人
type Originator struct {
    state string // 這裏就簡單一點,要保存的狀態就是一個字符串
}

func (o *Originator) SetState(s string) {
    o.state = s
}

func (o *Originator) GetState() string {
    return o.state
}

// 這裏就是規定了要保存的狀態範圍
func (o *Originator) CreateMemento() *Memento {
    return &Memento{state: o.state}
}
// 負責人
type Caretaker struct {
    memento *Memento
}

func (c *Caretaker) GetMemento() *Memento {
    return c.memento
}

func (c *Caretaker) SetMemento(m *Memento) {
    c.memento = m
}
import "fmt"
    // 建立一個發起人並設置初始狀態
    // 此時與備忘錄模式無關,只是模擬正常程序運行
    o := &Originator{state: "hello"}
    fmt.Println("當前狀態:",o.GetState())
    // 如今須要保存當前狀態
    // 就建立一個負責人來設置(通常來講,對於一個對象的同一個備忘範圍,應當只有一個負責人,這樣方便作多狀態多備忘管理)
    c := new(Caretaker)
    c.SetMemento(o.CreateMemento())

    o.SetState("world")
    fmt.Println("更改當前狀態:",o.GetState())

    // 恢復備忘
    o.SetState(c.GetMemento().GetState())
    fmt.Println("恢復後狀態",o.GetState())
當前狀態: hello
更改當前狀態: world
恢復後狀態 hello

模板方法模式

定義

模板模式(Template Pattern )又被稱做模板方法模式( Template Method Pattern),它是一種簡單的、常見的且應用很是普遍的模式。

英文定義以下:

Define the skeleton of an algorithm in an operation, deferring some steps tosubclasses. Template Method lets subclasses redefine certain steps of analgorithm without changing the algorithm' S structure.

意思是:定義一個操做中的算法的框架,而將一些 步驟延遲到子類中。使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。簡單來講,就是爲子類設計一個模板,以便在子類中能夠複用這些方法。

類圖

模板模式包含以下角色:

  • 抽象模板(Abstract Template)角色:該角色定義一個或多個抽象操做,以便讓子類實現;這些抽象操做是基本操做,是一個頂級邏輯的組成步驟,該角色還須要定義一個或幾個模板方法(模板方法的介紹,請看下文) ;
  • 具體模板(Concrete Template) 角色:該角色實現抽象模板中定義的一個或多個抽象方法,每個抽象模板角色均可以有任意多個具體模板角色與之對應,而每個具體模板角色均可以給出這些抽象方法的不一樣實現,從而使得頂級邏輯的實現各不相同。

優缺點

模板模式的優勢:

  • 提升了代碼的複用性,將相同部分的代碼放在抽象的父類中;
  • 提升了拓展性:將不一樣的代碼放入不一樣的子類中,經過對子類的擴展增長新的行爲;符合開閉原則:行爲由父類控制,經過子類擴展新的行爲。

模板模式的缺點:

  • 每一個不一樣的行爲都要新增一個子類來完成,抽象類中的抽象方法越多,子類增長成本就越高。並且新增的子類越多,系統就越複雜。

應用場景

模板模式的典型應用場景以下:
多個子類有公共方法,而且邏輯基本相同時;

  • 能夠把重要的、複雜的、核心算法設計爲模板方法,其餘的相關細節功能則由各個子類實現;
  • 重構時,模板方法模式是-一個常用的模式,把相同的代碼抽取到父類中,而後經過鉤子函數約束其行爲。

使用實例

以生活中.上班的過程爲例,咱們上班的一般流程是:起牀洗漱->通勤(開車、坐公交、打車)
->到達公司。從以上步驟能夠看出,只有通勤部分是不同的,其餘都同樣,由於開車可能會
被限號,就只能打車或坐公交去公司了,下面咱們用代碼( 模板模式)來實現一下。

// 上班抽象模板接口
type AbstractWork interface {
    // 因爲go是面向組合的一種思惟,從語言層不支持聚合,因此聚合須要本身把接口變量傳入來實現
    GotoWork(work AbstractWork)
    Getup()
    Commute()
    Arrive()
}
// 上班抽象類
type AbsClass struct {}

func (a AbsClass) GotoWork(work AbstractWork) {
    a.Getup()
    work.Commute()
    a.Arrive()
}

func (a AbsClass) Getup() {
    fmt.Println("1. 起牀")
}

func (a AbsClass) Commute() {}

func (a AbsClass) Arrive() {
    fmt.Println("3. 到達")
}

type DriveToWork struct {
    AbsClass
}


func (d *DriveToWork) Commute() {
    fmt.Println("2. 開車去公司")
}

func (d *DriveToWork) GotoWork(work AbstractWork){
    d.AbsClass.GotoWork(d)
}

type BusToWork struct {
    AbsClass
}

func (d *BusToWork) Commute() {
    fmt.Println("2. 坐公交去公司")
}

func (d *BusToWork) GotoWork(work AbstractWork) {
    d.AbsClass.GotoWork(d)
}
var (
        work AbstractWork
    )
    work = &BusToWork{AbsClass{}}
    work.GotoWork(work)
    work = &DriveToWork{AbsClass{}}
    work.GotoWork(work)
=== RUN   TestAbsClass_GotoWork
1. 起牀
2. 坐公交去公司
3. 到達
1. 起牀
2. 開車去公司
3. 到達
--- PASS: TestAbsClass_GotoWork (0.00s)
PASS

狀態模式

定義

狀態模式( Allow an object to alter its behavior when its internal state changes.Theobject will appear to
change its class.)

翻譯過來就是:容許一個對象在其內部狀態改變時改變其行爲,這個對象看起來好像是改變了其類。狀態模式是一種對象行爲型模式。

類圖

狀態模式包含角色以下:

  • 上下文角色( Context) :上下文角色- -般是一個類,上下文角色會聚合不少和state, 這些state使用靜態常量修飾,而且負責state的狀態切換;另外上下文角色還會包含抽象狀態角色中定義的全部行爲如request,而後內部將請求委託給state的handle處理;
  • 抽象狀態角色(State) :抽象狀態角色通常是一個抽象類,用來定義具體狀態的公共行爲好比handle,任何具體狀態都必須實現該抽象類中的抽象方法;
  • 具體狀態角色( ConcreteState) :繼承抽象狀態角色,實現抽象方法,實際處理來自Context的委託請求,當Context改變狀態時行爲也跟着改變。

優缺點

狀態模式的優缺點總結以下:

  • 減小代碼體積,利於拓展:狀態模式能夠消除繁雜的條件判斷語句塊,使得業務邏輯清晰,很好地應對對象狀態的增長、刪除的業務場景,由於添加新的狀態只須要增長新的狀態類就行了;
  • 狀態模式狀態不少時會致使狀態類比較多,子類太多的時候就不方便維護管理了。

應用場景

狀態模式的應用場景以下:

  • 行爲隨狀態改變而改變的場景;
  • 化繁爲簡,若是代碼中包含大量的條件語句塊好比switch..case. if等,這些語句塊的出現會致使業務邏輯變動時代碼塊也會變動,對狀態的增長、刪除時的調整修改起來比較吃力時就能夠考慮狀態模式;

使用實例,電視的操做

首先看看不引入狀態模式時,咱們要使用多少的switch case

import "fmt"
// 定義電視狀態
const (
    STANDBY_STATE = 1
    POWER_OFF_STATE = 2
    PLAY_STATE = 3
)
type ITelevision interface {
    // 開機
    PowerOn()
    // 關機
    PowerOff()
    // 播放
    Play()
    // 待機
    Standby()
}
// 電視類
type Telev struct {
    ITelevision
    state int
}

func (telev *Telev) State() int {
    return telev.state
}

func (telev *Telev) SetState(state int) {
    telev.state = state
}

func (t *Telev) PowerOn() {
    switch t.state {
        case STANDBY_STATE:
        case POWER_OFF_STATE:
            fmt.Println("開機")
            t.SetState(STANDBY_STATE)
        case PLAY_STATE:
        default:
    }
}
func (t *Telev) PowerOff() {
    // 待機和播放狀態均可以關機
    switch t.state {
    case STANDBY_STATE:
        fmt.Println("關機")
        t.SetState(POWER_OFF_STATE)
    case PLAY_STATE:
        fmt.Println("關機")
        t.SetState(POWER_OFF_STATE)
    case POWER_OFF_STATE:
    default:
    }
}
func (t *Telev) Play() {
    switch t.state {
    case STANDBY_STATE:
        fmt.Println("播放")
        t.SetState(PLAY_STATE)
    default:
    }
}
func (t *Telev) Standby() {
    switch t.state {
    case POWER_OFF_STATE:
        fmt.Println("關機")
        t.SetState(POWER_OFF_STATE)
    case PLAY_STATE:
        fmt.Println("待機")
        t.SetState(PLAY_STATE)
    default:
    }
}
tv := Telev{
        ITelevision: nil,
        state:       POWER_OFF_STATE,
    }
    // 這裏由於電視仍是關機狀態,因此不會有任何的輸出
    tv.Play()

    tv.PowerOn()
    tv.Play()
    tv.Standby()
    tv.PowerOff()
開機
播放
待機
關機
// 引入控制器(上下文角色)
type RemoteControlMachine struct {
    currentSate TVState
}

func (r *RemoteControlMachine) PowerOn() {
    r.currentSate.PowerOn(r)
}

func (r *RemoteControlMachine) PowerOff() {
    r.currentSate.PowerOff(r)
}

func (r *RemoteControlMachine) Play() {
    r.currentSate.Play(r)
}

func (r *RemoteControlMachine) Standby() {
    r.currentSate.Standby(r)
}

func (r *RemoteControlMachine) CurrentSate() TVState {
    return r.currentSate
}

func (r *RemoteControlMachine) SetCurrentSate(currentSate TVState) {
    r.currentSate = currentSate
}


// 電視狀態抽象接口
type TVState interface {
    // 開機
    PowerOn(r *RemoteControlMachine)
    // 關機
    PowerOff(r *RemoteControlMachine)
    // 播放
    Play(r *RemoteControlMachine)
    // 待機
    Standby(r *RemoteControlMachine)
}

// 待機狀態
type StandByState struct {
    r *RemoteControlMachine
}

func (s *StandByState) PowerOn(r *RemoteControlMachine) {}

func (s *StandByState) PowerOff(r *RemoteControlMachine) {
    fmt.Println("關機")
    // 使用遙控器設置電視機狀態爲關機
    s.r = r
    s.r.SetCurrentSate(&PowerOffState{})
    // 執行關機
    s.r.PowerOff()
}

func (s *StandByState) Play(r *RemoteControlMachine) {
    fmt.Println("播放")
    // 使用遙控器設置電視機狀態爲播放
    s.r = r
    s.r.SetCurrentSate(&PlayState{})
    // 執行播放
    s.r.Play()
}

func (s *StandByState) Standby(r *RemoteControlMachine) {
    // do nothing
}

// 關機狀態
type PowerOffState struct {
    r *RemoteControlMachine
}

func (s *PowerOffState) PowerOn(r *RemoteControlMachine) {
    fmt.Println("開機")
    // 使用遙控器設置電視機狀態爲開機
    s.r = r
    s.r.SetCurrentSate(&StandByState{})
    // 執行播放
    s.r.Standby()
}

func (s *PowerOffState) PowerOff(r *RemoteControlMachine) {
}

func (s *PowerOffState) Play(r *RemoteControlMachine) {
}

func (s PowerOffState) Standby(r *RemoteControlMachine) {
}

// 播放狀態
type PlayState struct {
    r *RemoteControlMachine
}

func (s *PlayState) PowerOn(r *RemoteControlMachine) {}

func (s *PlayState) PowerOff(r *RemoteControlMachine) {
    fmt.Println("關機")
    // 使用遙控器設置電視機狀態爲關機
    s.r = r
    s.r.SetCurrentSate(&PowerOffState{})
    // 執行關機
    s.r.PowerOff()
}

func (s *PlayState) Play(r *RemoteControlMachine) {
}

func (s *PlayState) Standby(r *RemoteControlMachine) {
    fmt.Println("開機")
    // 使用遙控器設置電視機狀態爲開機
    s.r = r
    s.r.SetCurrentSate(&StandByState{})
    // 執行播放
    s.r.Standby()
}
context := RemoteControlMachine{}

    context.SetCurrentSate(&PowerOffState{})
    // 若是直接播放,由於電視處於關機狀態,因此不會有輸出
    context.Play()

    context.PowerOn()
    context.Play()
    context.Standby()
    context.PowerOff()
=== RUN   TestTelev_Play
開機
播放
開機
關機
--- PASS: TestTelev_Play (0.00s)
PASS

能夠看到,測試結果沒有任何不一樣,可是咱們沒有寫一行switch..case語句塊,反而是將對象的各個狀態抽出來作成狀態類,而後各個狀態類在對各個行爲作出實現,代碼更加精簡。

狀態模式具體的狀態類在對狀態作出變動時其行爲也跟着作出變動,其實代碼量減小並不十分明顯,可是對於狀態拓展十分友好,只須要增長狀態類再實現各個行爲便可拓展新的狀態出來,也體現了開閉原則及單一職責原則;狀態模式將對象狀態的變動放到類的內部進行,外部調用者無需關心對象的狀態及行爲的變化,也體現了更好的封裝性;另外對代碼的cpd ( 代碼重複率檢測)也是頗有提高明顯。

總結

策略模式

定義

策略模式(Strategy Pattern: Define a family of algorithms,encapsulate each one,andmake them interchangeable.)

中文解釋爲:定義一組算法,而後將這些算法封裝起來,以便它們之間能夠互換,屬於一種對象行爲型模式。總的來講策略模式是一種比較簡單的模式,聽起來可能有點費勁,其實就是定義一組通用算法的上層接口,各個算法實現類實現該算法接口,封裝模塊使用相似於Context的概念,Context暴漏一組接口,Context內部接口委託到抽象算
法層。

類圖

包含的角色羅列以下:

  • 上下文角色(Context) :該角色通常是一個實現類或者封裝類,起到必定的封裝及隔離做用,實際接受請求並將請求委託給實際的算法實現類處理,避免外界對底層策略的直接訪問;
  • 抽象策略角色( Strategy) :該角色通常是一一個抽象角色,爲接口或者抽象類扮演,定義具體策略角色的公共接口;
  • 具體策略角色( ConcreteStrategy) :實現抽象策略角色的接口,爲策略的具體實現類。

優缺點

策略模式的優勢以下:

  • 全部策略放入一組抽象策略接口中,方便統一管理與實現;

策略模式的缺點以下:

  • 策略模式每種策略都是單獨類,策略不少時策略實現類也很可觀;
  • 客戶端初始化Context的時候須要指定策略類,這樣就要求客戶端要熟悉各個策略,對調用方要求較高。

應用場景

策略模式的應用場景以下:

  • 須要自由切換算法的場景
  • 須要屏蔽算法實現細節的場景

使用實例:鴨子模型

類圖:

import "fmt"

type FlyBehavior interface {
    Fly()
}

type QuackBehavior interface {
    Quack()
}

type Duck struct {
    fly FlyBehavior
    quack QuackBehavior
}

func (d *Duck)Swim() {
    fmt.Println("鴨子游泳")
}

func (d *Duck) Display (behavior FlyBehavior,quackBehavior QuackBehavior) {
    behavior.Fly()
    quackBehavior.Quack()
}

type FlyWithWings struct {}

func (f *FlyWithWings) Fly ()  {
    fmt.Println("鴨子用翅膀飛")
}

type FlyNoWay struct {}

func (f *FlyNoWay) Fly ()  {
    fmt.Println("鴨子飛不起來")
}

type Quack struct {}

func (f *Quack) Quack ()  {
    fmt.Println("鴨子嘎嘎叫")
}

type Squeak struct {}

func (f *Squeak) Quack ()  {
    fmt.Println("鴨子咔咔叫")
}

type Mute struct {}

func (f *Mute) Quack ()  {
    fmt.Println("鴨子不能叫")
}

type ReadHead struct {
    *Duck
    fly *FlyWithWings
    quack *Quack
}

func (r *ReadHead) Display ()  {
    r.Swim()
    r.Duck.Display(r.fly, r.quack)
}

type Wooden struct {
    *Duck
    fly *FlyNoWay
    quack *Mute
}

func (r *Wooden) Display ()  {
    r.Swim()
    r.Duck.Display(r.fly,r.quack)
}

type Mallard struct {
    *Duck
    fly *FlyWithWings
    quack *Quack
}

func (m *Mallard) Display ()  {
    m.Swim()
    m.Duck.Display(m.fly, m.quack)
}

type Rubber struct {
    *Duck
    fly *FlyNoWay
    quack *Squeak
}

func (r *Rubber) Display ()  {
    r.Swim()
    r.Duck.Display(r.fly, r.quack)
}
flynoway := &FlyNoWay{}
    flayWihtwings := &FlyWithWings{}
    quack := &Quack{}
    sqeak := &Squeak{}
    mute := &Mute{}
    duck := ReadHead{
        Duck:  &Duck{},
        fly:   flayWihtwings,
        quack: quack,
    }
    duck.Display()
    mallard := Mallard {
        Duck:  &Duck{},
        fly:   flayWihtwings,
        quack: quack,
    }
    mallard.Display()
    rub := Rubber {
        Duck:  &Duck{},
        fly:   flynoway,
        quack: sqeak,
    }
    rub.Display()
    wooden := Wooden{
        Duck:  &Duck{},
        fly:   flynoway,
        quack: mute,
    }
    wooden.Display()
鴨子游泳
鴨子用翅膀飛
鴨子嘎嘎叫
鴨子游泳
鴨子用翅膀飛
鴨子嘎嘎叫
鴨子游泳
鴨子飛不起來
鴨子咔咔叫
鴨子游泳
鴨子飛不起來
鴨子不能叫

觀察者模式

定義

觀察者模式( Observer Pattern)也稱發佈閱模式。

觀察者模式的英文定義以下:

Define a one-to-many dependency between objects so that when one objectchanges state, all its dependents are notified and updated automatically.

意思是:定義對象間一種一對多的依賴關係,使得每當一個對象改變狀態,則全部依賴於它的對象都會獲得通知並被自動更新。

以生活中的例子來講,就像咱們訂閱報紙同樣,天天有多少人訂閱,當有新報紙發佈的時候,就會有多少人收到新發布的報紙,這種模式就是訂閱一發布模式,而報社和訂閱者就知足定義中說的,一對多的依賴關係。

類圖

觀察者模式包含以下角色:

  • 抽象主題(Subject) 角色:該角色又稱爲「發佈者」或」被觀察者,能夠增長和刪除觀察者對象;
  • 具體主題( Concrete Subject) 角色:該角色又稱爲「具體發佈者」或「具體被觀察者」,它將有關狀態存入具體觀察者對象,在具體主題的內部狀態改變時,給全部登記過(關聯了觀察關係)的觀察者發出通知;
  • 抽象觀察者(Observer) 角色:該角色又稱爲「訂閱者」,定義一個接收通知的接口,在獲得主題的通知時更新本身;
  • 具體觀察者( Concrete Observer)角色:該角色又稱爲「 具體訂閱者」,它會實現-個接收通知的方法,用來使自身的狀態與主題的狀態相協調。

優缺點

觀察者模式的優勢:

  • 觀察者和被觀察者之間,實現了抽象耦合。被觀察者角色所知道的只是- 個具體觀察者集合,每個具體觀察者都符合一個抽象觀察者的接口。被觀察者並不認識任何一個具體的觀察者,它只知道它們都有一個共同的接口。因爲被觀察者和觀察者沒有緊密的耦合在一塊兒,所以它們能夠屬於不一樣的抽象化層次,且都很是容易擴展;
  • 此模式爲廣播模式,全部的觀察者只須要訂閱相應的主題,就能收到此主題下的全部廣播。

觀察者模式的缺點:

  • 觀察者只知道被觀察者會發生變化,但不知道什麼時候會發生變化;
  • 若是主題之間有循環依賴,會致使系統崩潰,因此在使用時要特別注意此種狀況;
  • 若是有不少個觀察者,則每一個通知會比較耗時。

應用場景

使用觀察模式的典型應用場景以下:

  • 關聯行爲的場景,例如,在一個系統中,若是用戶完善了我的資料,就會增長積分、添加日誌、開放一些功能權限等,就比較適合用觀察者模式;
  • 消息隊列,例如,須要隔離發佈者和訂閱者,須要處理一對多關係的時候。

使用實例

以生活中的讀者訂閱爲例,假設,讀者A和讀者B訂閱了某平臺的圖書,當有新的圖書發佈時就會給兩位讀者發送圖書,實現代碼以下。

import "fmt"

// 讀者接口(訂閱接口)
type IReader interface {
    Update(bookName string)
}

// 讀者類(訂閱者)
type Reader struct {
    name string
}

func (r *Reader) Update(bookName string) {
    fmt.Println(r.name,"-收到了圖書",bookName)
}

// 平臺接口(發佈方接口)
type IPlatform interface {
    Attach(reader IReader)
    Detach(reader IReader)
    NotifyObservers(bookName string)
}

// 具體發佈類(發佈方)
type Platform struct {
    list []IReader
}

func (p *Platform) Attach(reader IReader) {
    // 增長讀者(訂閱者)
    p.list = append(p.list, reader)
}

func (p *Platform) Detach(reader IReader) {
    // 刪除讀者(訂閱者)
    for i,v := range p.list {
        if v == reader {
            // 刪除第i個元素,由於interface類型在golang中
            // 以地址的方式傳遞,因此能夠直接比較進行刪除
            // golang中只要記得byte,int,bool,string,數組,結構體,默認傳值,其餘的默認傳地址便可
            p.list = append(p.list[:i],p.list[i+1:]...)
        }
    }
}

func (p *Platform) NotifyObservers(bookName string) {
    // 通知全部讀者
    for _,reader := range p.list {
        reader.Update(bookName)
    }
}

func (p *Platform) Change (bookName string)  {
    p.NotifyObservers(bookName)
}
// 建立圖書平臺(發佈者)
    platform := Platform{list: []IReader{}}
    // 建立讀者A
    reader := Reader{name:"A"}
    // 讀者A訂閱圖書通知
    platform.Attach(&reader)
    // 建立讀者B
    reader2 := Reader{name:"B"}
    // 讀者B訂閱圖書通知
    platform.Attach(&reader2)
    platform.Change("《go核心編程》")
    // 讀者B取消訂閱
    platform.Detach(&reader2)
    platform.Change("《go高級編程》")
A -收到了圖書 《go核心編程》
B -收到了圖書 《go核心編程》
A -收到了圖書 《go高級編程》

解釋器模式

定義

解釋器模式( Interpreter Pattern )提供了評估語言的語法或者表達式的方式,屬於一種行爲型的設計模式。
解釋器模式的英文原話是:

Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.

意思是:給定一門語言,定義它的文法的一種表示,並定義一個解釋器,該解釋器使用該表示來解釋語言中的句子。簡單來講,就是咱們能夠定義一種語法好比就是一個表達式如: a-b+c, 起初咱們並不知道這個句子想要攜帶信息或者執行什麼操做,而後咱們要定義一個解析器來進行表達式解析,以便獲得正確的結果。

對於表達式: a-b+c 這種,咱們作個簡單的分析, a、b、c這種咱們又叫作運算參數,+、-符號這種咱們稱之爲運算符號,也就說這類表達式咱們能夠將其抽象爲兩種角色:運算參數、運算符號。運算參數通常就是英文字母,執行時各個參數須要賦上具體的數字值去替代英文字母執行,運算參數有一個共同點就是無論是a、b或者其它參數,除了被賦值以外不須要作其它任何處理,是執行時的最小單元,在解釋器模式中被稱爲終結符號。運算符號是進行運算時具體要被解釋器解釋執行的部分,想象一下,加入咱們計算機不知道如何處理相似+、-這種符號,咱們是不要針對每個符號寫一個解釋方法,以便告訴計算機該符號須要進行何種操做,這也就是解釋器模式的核心——須要完成邏輯的解釋執行操做,而運算符號在解釋器模式中也被稱爲非終結符號。

類圖

一般包含以下角色:

  • 抽象解釋器( AbstractExpression) :抽象解釋器是一個上層抽象類,用來抽取定義公共的解釋方法: interpreter, 具體的解釋任務交給子類去完成; .
  • 終結符表達式( TerminalExpression) :是抽象解釋器的子類,實現了與文法中的元素相關的解釋操做。通常模式中只會有一個終結符表達式也就是終結符的類,可是會有多個實例,好比: a、b、c,這些終結符號能夠任意多種可是隻有一個類來描述;
  • 非終結符表達式( NonTerminalExpression) :也是抽象解釋器的子類,用來實現文法中與終結符相關的操做。該角色通常會有多個實現類,好比+、-運算符號就各自對應一種類實現,分別對應加法解釋類和減法解釋類,非終結符表達式的類的個數通常會有不少 ,由於咱們可執行的操做通常會有不少,這也從側面加重了該模式下類設計的複雜性;
  • 上下文(Context) :上下文通常用來定義各個解釋器須要的數據或公共功能,好比上面的表達式,咱們使用上下文來保存各個參數的值,-般是- 個HashMap對象,以便後面全部解釋器均可以使用該上下文來獲取參數值;

優缺點

解釋器模式的優勢:

  • 拓展性強:修改文法規則只須要修改相應的非終結符表達式就能夠了,即增長非終結符類就能夠了。

解釋器模式的缺點:

  • 採用遞歸調用方法,不利於調試,增長了系統的複雜性以及下降了系統執行的效率;
  • 解釋器模式比較容易形成類設計的膨脹,主要是非終結符表達式類會隨着系統的複雜性而膨脹;
  • 可利用的場景比較少;
  • 對於比較複雜的文法很差解析。

應用場景

  • 一個簡單語法須要解釋的場景,如: sql語法分析,用來解析那種比較標準的字符集;
  • 重複發生的問題可使用解釋器模式,如:日誌分析,日誌分析時基礎數據是相同的相似於咱們的終結符,可是日誌格式每每是各異的,相似於非終結符,只須要指定具體的實現類便可。

使用實例

如今咱們以一個最簡單的例子: a+b,咱們要作的就是解釋執行這段語法文本,a和b是兩個字母也叫作兩個變量,咱們須要使用一個「+」符號來將這倆變量鏈接起來,假設咱們的語言並不知道符號"+"是什麼做用,具體做用須要咱們去實現(假設咱們並不知道+實際上是加法的意思),示例比較簡單,只是爲了說明解釋器模式沒別的意思。

import "bytes"

type Context struct {
    text string
}

//抽象解釋器
type AbstractExpress interface {
    Interpreter(*Context) int
}

// 終結符,即咱們的參數構造類
type TerminalExpression struct {
    arg int
}

func (t *TerminalExpression) Interpreter(ctx *Context) int {
    return t.arg
}

// 非終結符,即咱們的運算符構造類
type NonTerminalExpression struct {
    left AbstractExpress
    right AbstractExpress
}

func (n NonTerminalExpression) Interpreter(ctx *Context) int {
    // 實現具體的a+b的解釋執行操做
    if !bytes.Equal([]byte(ctx.text),[]byte("")) {
        return n.left.Interpreter(ctx) + n.right.Interpreter(ctx)
    }
    return 0
}
import "fmt"
var (
        left AbstractExpress
        right AbstractExpress
        callExpression AbstractExpress
    )
    left = &TerminalExpression{arg:12}
    right = &TerminalExpression{arg:34}
    callExpression = &NonTerminalExpression{left:left,right:right}

    context := &Context{text:"+"}

    result := callExpression.Interpreter(context)
    fmt.Println(result)
46

總結

責任鏈模式

定義

什麼是責任鏈模式?生活中咱們常常遇到這樣的問題,好比請假審批須要層層上報處理、遇到問題各個部門甩賴扯皮,像這種,在事情沒有被處理以前,會通過一系列階段,相似於「踢皮球」似的。一樣地,當一個請求到達時,在程序沒法直接決定由哪一個對象負責處理時,客戶的請求就會造成一種鏈式傳遞,在鏈上的各個處理對象若是沒法直接決定是否由其處理時,就會將請求再傳遞至下一個鏈對象,直到請求被處理或者被丟棄等等。這種處理鏈咱們形象稱其爲「責任鏈」

責任鏈模式的定義是:使多個對象都有機會處理請求,從而避免了請求的發送者和接受者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有對象處理它爲止。

類圖

責任鏈模式的核心就是Handler鏈抽象對象,該對象包含一個指向下一個鏈對象的私有屬性,「鏈」是責任鏈的核心,就是使用該屬性進行鏈式調用實現的。責任鏈模式的包含的角色以下:

  • 請求者( Client) : Client 角色就是向鏈發送請求的角色;
  • 責任人的抽象類角色( Handler) : Handler角色是模式的核心,Handler知道下一個責任人是誰,並根據責任人的處理能力選擇是否將請求轉發至下一個責任人。
  • 具體責任人對象( ConcreteHandler) :該角色是具體處理請求的對象.

使用實例

舉個例子,初級、中級、高級開發工程師分別處理問題的能力不一樣,咱們假設初級工程師只能處理難度級別爲1的問題,中級工程師能處理難度爲一、2的問題,高級工程師能處理難度級別爲一、二、3的問題,另外咱們有一個Request請求表明要處理的請求,內部包含一個難度級別和要請求的內容,咱們先來看下類圖的設計:

import "fmt"

const (
    DIFFICULTY_LEVEL_1 = 1
    DIFFICULTY_LEVEL_2 = 2
    DIFFICULTY_LEVEL_3 = 3
)

type HandleMessage func(request IRequest)


type IRequest interface {
    // 請求級別
    GetRequestLevel() int
    // 獲取要請求的內容
    GetRequest() string
}

type Request struct {
    // 難度1--初級工程師
    // 難度2--中級工程師
    // 難度3--高級工程師
    level int
    request string
}

func InitRequset(level int, request string) *Request {
    r := &Request{
        level:   level,
        request: request,
    }
    switch r.level {
    case 1:
        r.request = "難度級別1的請求是:"+ request
    case 2:
        r.request = "難度級別2的請求是:"+ request
    case 3:
        r.request = "難度級別3的請求是:"+ request
    }
    return r
}

func (r Request) GetRequestLevel() int {
    return r.level
}

func (r Request) GetRequest() string {
    return r.request
}

type Handler interface {
    HandleMessage(request IRequest)
    SetNextHandler(handler Handler)
    Response(request IRequest)
    GetLevel()int
    GetNext() Handler
}

// 初級工程師
type Primary struct {
    level int
    request string
    next Handler
}

func (p *Primary) GetNext() Handler {
    return p.next
}

func (p *Primary) GetLevel() int {
    return p.level
}

func (p *Primary) HandleMessage(request IRequest) {
    message := func(request IRequest)  {
        // 若是請求級別小於能夠處理的級別就直接處理
        if request.GetRequestLevel() <= p.GetLevel() {
            p.Response(request)
        } else {
            if p.GetNext() != nil {
                p.next.HandleMessage(request)
            } else {
                fmt.Println("---難度級別爲",request.GetRequestLevel(),"的請求沒法處理")
            }
        }
    }
    message(request)
}

func (p *Primary) SetNextHandler(handler Handler) {
    p.next = handler
}

func (p *Primary) Response(request IRequest) {
    fmt.Println("---難度級別1的請求---")
    fmt.Printf(request.GetRequest())
    fmt.Println("初級工程師已經處理完畢")
}

func InitPrimary() Handler {
    return &Primary{
        level:   DIFFICULTY_LEVEL_1,
        request: "",
    }
}

type Middle struct {
    level int
    request string
    next Handler
}

func (p *Middle) HandleMessage(request IRequest) {
    message := func(request IRequest)  {
        // 若是請求級別小於能夠處理的級別就直接處理
        if request.GetRequestLevel() <= p.GetLevel() {
            p.Response(request)
        } else {
            if p.GetNext() != nil {
                p.next.HandleMessage(request)
            } else {
                fmt.Println("---難度級別爲",request.GetRequestLevel(),"的請求沒法處理")
            }
        }
    }
    message(request)
}

func (p *Middle) SetNextHandler(handler Handler) {
    p.next = handler
}

func (p *Middle) Response(request IRequest) {
    fmt.Println("---難度級別2的請求---")
    fmt.Printf(request.GetRequest())
    fmt.Println("中級工程師已經處理完畢")
}

func (p *Middle) GetLevel() int {
    return p.level
}

func (p *Middle) GetNext() Handler {
    return p.next
}

type Senior struct {
    level int
    request string
    next Handler
}

func (p *Senior) HandleMessage(request IRequest) {
    message := func(request IRequest)  {
        // 若是請求級別小於能夠處理的級別就直接處理
        if request.GetRequestLevel() <= p.GetLevel() {
            p.Response(request)
        } else {
            if p.GetNext() != nil {
                p.next.HandleMessage(request)
            } else {
                fmt.Println("---難度級別爲",request.GetRequestLevel(),"的請求沒法處理")
            }
        }
    }
    message(request)
}

func (p *Senior) SetNextHandler(handler Handler) {
    p.next = handler
}

func (p *Senior) Response(request IRequest) {
    fmt.Println("---難度級別3的請求---")
    fmt.Printf(request.GetRequest())
    fmt.Println("高級工程師已經處理完畢")
}

func (p *Senior) GetLevel() int {
    return p.level
}

func (p *Senior) GetNext() Handler {
    return p.next
}
var (
        pri Handler
        mid Handler
        sen Handler
        list []IRequest
    )
    list = make([]IRequest,0)
    list = append(list,&Request{
        level:   DIFFICULTY_LEVEL_1,
        request: "1+1=?",
    })
    list = append(list,&Request{
        level:   DIFFICULTY_LEVEL_2,
        request: "4*3",
    })
    list = append(list,&Request{
        level:   DIFFICULTY_LEVEL_3,
        request: "99*99",
    })
    list = append(list,&Request{
        level:   4,
        request: "aaaaaaaaaaa",
    })
    pri = InitPrimary()
    mid = &Middle{
        level:   DIFFICULTY_LEVEL_2,
        request: "",
        next:    nil,
    }
    sen = &Senior{
        level:   DIFFICULTY_LEVEL_3,
        request: "",
        next:    nil,
    }

    // 設置鏈的順序
    pri.SetNextHandler(mid)
    mid.SetNextHandler(sen)
    for _,v := range list {
        // 責任鏈中處理該請求
        pri.HandleMessage(v)
    }
=== RUN   TestInitPrimary
---難度級別1的請求---
1+1=?初級工程師已經處理完畢
---難度級別2的請求---
4*3中級工程師已經處理完畢
---難度級別3的請求---
99*99高級工程師已經處理完畢
---難度級別爲 4 的請求沒法處理
--- PASS: TestInitPrimary (0.00s)
PASS

迭代器模式

定義

迭代器模式的英文定義以下:

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

意思是:提供一種方法訪問一個容器對象中各個元素,而又不需暴露該對象的內部細節。迭代器是爲容器服務的,容器是指用來容納其餘對象的對象,例如,Collection集合類型、Set類等。

類圖

迭代器模式有如下4個角色:

  • 抽象迭代器(Iterator) 角色:該角色負責定義訪問和遍歷元素的接口;
  • 具體迭代器( Concrete Iterator) 角色:該角色實現Iterator接口,完成容器元素的遍歷;
  • 抽象彙集(Aggregate) 角色:該角色提供建立迭代器角色的接口;
  • 具體彙集(Concrete Aggregate) 角色:該角色實現抽象彙集接口,建立出容納迭代器的對象。

優缺點

迭代器模式的優勢:

  • 迭代器模式將數據存儲和數據遍歷的職責進行分離;
  • 迭代器模式簡化了遍歷容器元素的操做;
  • 迭代器模式使不一樣的容器,具有一個統一的遍歷接口;
  • 迭代器模式封裝了遍歷算法,使算法獨立於彙集角色,調用者無須知道彙集對象的類型,即便彙集對象的類型發生變化,也不會影響遍歷過程。

迭代器模式的缺點:

  • 因爲迭代器模式將數據存儲和數據遍歷的職責進行分離,若是增長新的聚合類,同時須要增長與之相對,應的迭代器類,這使得類的個數會成對增長,在某種程度上來講增長了系統的複雜性。

應用場景

迭代器的應用很普遍,已經發展成爲程序開發中最基礎的工具類了。在Java語言中,從JDK1.2開始,增長了java.til.lterator 接口,並將Iterator 應用到各個彙集類( Collection)中,如ArrayList、 Vector、Stack、HashSet 等集合類都實現iterator() 方法,返回一個迭代器Iterator,用於對集合中的元素進行遍歷。這使咱們在項目中無須在獨立地寫迭代器,直接使用便可,這樣既輕鬆又便捷。
注意:要儘量地使用編程語言自身提供的迭代器,而非本身寫的迭代器。

// 抽象迭代器
type Iterator interface {
    Next() interface{}
    HasNext() bool
}

// 具體迭代器
type ConcreteIterator struct {
    index int
    size int
    con Aggregate
}

func (c *ConcreteIterator) HasNext() bool {
    return c.index < c.size
}

func (c *ConcreteIterator) Next() interface{} {
    if c.HasNext() {
        res := c.con.GetElement(c.index)
        c.index++
        return res
    }
    return nil
}



// 抽象彙集
type Aggregate interface {
    Add(obj interface{})
    CreateIterator() Iterator
    GetElement(index int) interface{}
    Size() int
}

// 具體彙集
type ConcreteAggregate struct {
    //私有存儲容器
    docker []interface{}
}

func (c *ConcreteAggregate) Size() int {
    return len(c.docker)
}

func (c *ConcreteAggregate) Add(obj interface{}) {
    c.docker = append(c.docker,obj)
}

func (c *ConcreteAggregate) CreateIterator() Iterator {
    return &ConcreteIterator{
        index: 0,
        size:  c.Size(),
        con:   c,
    }
}

func (c *ConcreteAggregate) GetElement(index int) interface{} {
    return c.docker[index]
}
// 定義聚族對象
    var (
        aggregate Aggregate
        iter Iterator
    )
    aggregate = &ConcreteAggregate{docker: []interface{}{}}
    aggregate.Add("java")
    aggregate.Add("Golang")
    aggregate.Add("Python")
    // 遍歷
    iter = aggregate.CreateIterator()
    for iter.HasNext() {
        fmt.Println(iter.Next())
    }
=== RUN   TestConcreteAggregate_Add
java
Golang
Python
--- PASS: TestConcreteAggregate_Add (0.00s)
PASS

若是您以爲本篇文章不錯,請記獲得個人GitHub點上一個star,您的支持是我最大的動力!十分感謝!

相關文章
相關標籤/搜索