经典设计模式(Golang)

状态模式

Posted by Dingding on August 3, 2019

概述

  • 意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
  • 主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
  • 何时使用:代码中包含大量与对象状态有关的条件语句。
  • 如何解决:将各种具体的状态类抽象出来。
  • 优点
    1. 封装了转换规则
    2. 枚举可能的状态,在枚举状态之前需要确定状态种类。
    3. 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
    4. 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
    5. 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
  • 缺点
    1. 状态模式的使用必然会增加系统类和对象的个数。
    2. 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
    3. 状态模式对”开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
  • 使用场景
    1. 行为随状态改变而改变的场景。
    2. 条件、分支语句的代替者。
  • 注意事项
    1. 在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。

UML

image

实现

package main

import (
	"fmt"
)

// Machine 机器
type Machine struct {
	count int
	state State

	noCandy  State
	hasCandy State
	paid     State
}

// 将对机器的请求,转发到状态的相应方法上
func (m *Machine) AddCandy(count int) {
	m.state.AddCandy(count)
}
func (m *Machine) PayMoney() {
	m.state.PayMoney()
}
func (m *Machine) OutCandy() {
	m.state.OutCandy()
}

// 初始化机器状态
func NewMachine() *Machine {
	m := &Machine{}
	m.noCandy = &NoCandy{machine: m}
	m.hasCandy = &HasCandy{machine: m}
	m.paid = &Paid{machine: m}

	m.count = 0
	m.state = m.noCandy

	return m
}

// State 状态接口
type State interface {
	AddCandy(count int) //服务员添加糖果
	PayMoney()          //用户付钱
	OutCandy()          //用户选择出货
}

// NoCandy 状态
type NoCandy struct {
	machine *Machine
}

func (n *NoCandy) AddCandy(count int) {
	n.machine.count += count
	n.machine.state = n.machine.hasCandy

	fmt.Println("add candy num:", count, "total remained:", n.machine.count)
}
func (n *NoCandy) PayMoney() {
	fmt.Println("there is no candy!")
}
func (n *NoCandy) OutCandy() {
	fmt.Println("there is no candy!")
}

// HasCandy 状态
type HasCandy struct {
	machine *Machine
}

func (n *HasCandy) AddCandy(count int) {
	n.machine.count += count

	fmt.Println("add candy num:", count, "total remained:", n.machine.count)
}
func (n *HasCandy) PayMoney() {
	n.machine.state = n.machine.paid
	fmt.Println("you has paid,please select candy.")

}
func (n *HasCandy) OutCandy() {
	fmt.Println("you need pay money first.")
}

// Paid 状态
type Paid struct {
	machine *Machine
}

func (n *Paid) AddCandy(count int) {
	n.machine.count += count
	fmt.Println("add candy num:", count, "total remained:", n.machine.count)
}
func (n *Paid) PayMoney() {
	fmt.Println("you has paid,don't pay again,please select candy.")
}
func (n *Paid) OutCandy() { //注意对状态的维护,这是重点
	n.machine.count--
	if n.machine.count > 0 {
		n.machine.state = n.machine.hasCandy
	} else {
		n.machine.state = n.machine.noCandy
	}
	fmt.Println("out a candy,total remained:", n.machine.count)
}

func main() {
	m := NewMachine()
	m.AddCandy(2)
	//付钱,出糖果
	m.PayMoney()
	m.OutCandy()
	//付钱,出糖果
	m.PayMoney()
	m.OutCandy()

	//无货:付钱,出糖果
	m.PayMoney()
	m.OutCandy()

	//添加糖果
	m.AddCandy(2)
	//付钱,付钱
	m.PayMoney()
	m.PayMoney()
	//出糖果,出糖果
	m.OutCandy()
	m.OutCandy()
}

UML 代码

@startuml
class Machine {
-int count 
-State state
-State noCandy
-State hasCandy
-State paid

+AddCandy(int count)
+PayMoney()
+OutCandy()
}

State o.. Machine

rectangle 状态{

interface State{
+AddCandy(int count)
+PayMoney()
+OutCandy()
}

class NoCandy{
-Machine machine
+AddCandy(int count)
+PayMoney()
+OutCandy()
}

class HasCandy{
-Machine machine
+AddCandy(int count)
+PayMoney()
+OutCandy()
}

class Paid{
-Machine machine
+AddCandy(int count)
+PayMoney()
+OutCandy()
}

State <|.. NoCandy
State <|.. HasCandy
State <|.. Paid
}
@enduml