设计模式笔记 - 状态模式

151 阅读2分钟

状态模式是一种行为设计模式,让你能在一个对象的内部状态变化时改变其行为,使其看上去就像改变了自身所属的类一样。


解决什么问题?

  • 如果对象需要根据自身当前状态进行不同行为, 同时状态的数量非常多且与状态相关的代码会频繁变更的话, 可使用状态模式。
  • 如果某个类需要根据成员变量的当前值改变自身行为, 从而需要使用大量的条件语句时, 可使用该模式。
  • 当相似状态和基于条件的状态机转换中存在许多重复代码时, 可使用状态模式。

优势:
  • 单一职责原则。 将与特定状态相关的代码放在单独的类中。
  • 开闭原则。 无需修改已有状态类和上下文就能引入新状态。
  • 通过消除臃肿的状态机条件语句简化上下文代码。

劣势:
  • 如果状态机只有很少的几个状态, 或者很少发生改变, 那么应用该模式可能会显得小题大作。

实现步骤:

  • 定义实体类接口
  • 定义实体类,关联实体类上下文类实体类可修改上下文类的状态
  • 定义上下文类,关联上下文类实体类。通过上下类接口调用实体类接口
  • 客户端代码关联实体类对象上下文类对象,实现状态启动和转移

下面是实现代码:

package main

import "fmt"

// 定义实体类接口
type state interface {
	render()
	publish() // 状态转移函数
}

// 定义上下文类
type document struct {
	s       state
	isUser  bool
	content string
}

func (d *document) render() {
	d.s.render()
}

func (d *document) publish() {
	d.s.publish()
}

// 关联实体类
func (d *document) changeState(s state) {
	d.s = s
}

// 定义实体类
type draft struct {
	d *document
}

func (d *draft) render() {
	fmt.Println("draft render: ", d.d.content)
}

func (d *draft) publish() {
	if d.d.isUser {
		d.d.changeState(&moderation{
			d: d.d,
		})
	} else {
		d.d.changeState(&published{
			d: d.d,
		})
	}

}

type moderation struct {
	d *document
}

func (m *moderation) render() {
	fmt.Println("moderation render: ", m.d.content)
}

func (m *moderation) publish() {
	if len(m.d.content) > 5 {
		m.d.changeState(&published{
			d: m.d,
		})
	} else {
		fmt.Println("moderation failed. content is shorter than 5")
		m.d.changeState(&draft{
			d: m.d,
		})
	}
}

type published struct {
	d *document
}

func (p *published) render() {
	fmt.Println("published render: ", p.d.content)
}

func (p *published) publish() {
	if len(p.d.content) < 10 {
		fmt.Println("published failed. content is shorter than 10")
		p.d.changeState(&draft{
			d: p.d,
		})
	} else {
		fmt.Println("published successfully!!!")
	}
}

func main() {
	doc := document{
		isUser:  true,
		content: "123",
	}
	dr := draft{d: &doc}
	doc.s = &dr

    // 第一次发布
    fmt.Println("first publish")
	doc.render()
	doc.publish()
	doc.render()
	doc.publish()
    fmt.Println()

    // 第二次发布
    fmt.Println("second publish")
    doc.content = "123456"
	doc.render()
    doc.publish()
	doc.render()
	doc.publish()
	doc.render()
	doc.publish()
    fmt.Println()

    // 第三此发布
    fmt.Println("third publish")
    doc.content = "123456789A"
	doc.render()
    doc.publish()
	doc.render()
	doc.publish()
	doc.render()
	doc.publish()
}

输出:

first publish
draft render:  123
moderation render:  123
moderation failed. content is shorter than 5

second publish
draft render:  123456
moderation render:  123456
published render:  123456
published failed. content is shorter than 10

third publish
draft render:  123456789A
moderation render:  123456789A
published render:  123456789A
published successfully!!!

参考