接口是一种类型,也是一种抽象结构,不会暴露所含数据的格式,类型及结构
格式:
type 接口类型名 interface {
方法名1(参数列表)返回值列表1
方法名2(参数列表)返回值列表2
...
}
接口类型名:命名时会在命名单词后加er, eg: Writer
方法名: 当方法首字母大写时,接口类型名首字母大写时,这个方法可以被接口所在包(package)之外的代码访问
参数列表、返回值列表:列表中的参数变量名可以被忽略
type Write interface {
Write([]byte) error
}
接口定义后,需要实现接口,调用方才能正确编译并使用接口
接口实现遵循2条规则
DataWriter interface{} 里面WriteData(data interface{}) error方法 要与 实现的 func (d *file) WriteData(data interface{}) error {} 方法名称、参数列表、返回参数列表中任意一项要一致,否则不会被实现
package main
?
import(
"fmt"
)
//定义一个数据写入盘
type DataWriter interface {
WriteData(data interface{}) error
}
//定义文件结构,用于实现DataWriter
type file struct {
}
// 实现DataWriter接口WriteData()方法
func (d *file) WriteData(data interface{}) error {
//模拟写入数据
fmt.Println("WriteData:", data)
return nil
}
func main() {
// 实例化file
f := new(file)
// 声明一个DataWriter的接口
var writer DataWriter
// 将接口赋值f, 也就是*file类型
writer = f
// 使用DataWriter接口进行数据写入
writer.WriteData("data")
}
实现失败例子:
函数名不一致导致报错
?
...
// 修改file方法DataWriterX()
func (d *file) WriteDataX(data interface{}) error {
//模拟写入数据
fmt.Println("WriteData:", data)
return nil
}
func main() {
// 实例化file
f := new(file)
// 声明一个DataWriter的接口
var writer DataWriter
// 将接口赋值f, 也就是*file类型
writer = f
// 使用DataWriter接口进行数据写入
writer.WriteData("data")
}
报错:
{ ...
"message": "cannot use f (type *file) as type DataWriter in assignment:\n\t*file does not implement DataWriter (missing WriteData method)",
...
}
实现接口的方法签名不一致导致的报错
把data参数的类型从interface{}修改为int类型
// 实现DataWriter接口WriteData()方法
func (d *file) WriteData(data int) error {
//模拟写入数据
fmt.Println("WriteData:", data)
return nil
}
编译报错:
{
...
"message": "cannot use f (type *file) as type DataWriter in assignment:\n\t*file does not implement DataWriter (wrong type for WriteData method)\n\t\thave WriteData(int) error\n\t\twant WriteData(interface {}) error",
...
}
//定义一个数据写入盘
type DataWriter interface {
WriteData(data interface{}) error
CanWrite() bool
}
报错:
{
...
"message": "cannot use f (type *file) as type DataWriter in assignment:\n\t*file does not implement DataWriter (missing CanWrite method)",
...
}
go接口实现时隐式,无须让实现接口的类型写出实现了哪些接口,这种设计称为非侵入式设计,让实现者所有类型均时平行的
类型与接口之间有一对多和多对一的关系
比如:定义一个socket 结构体类型,可以实现 i o.Writer() 接口和io.Close()方法,这两个接口彼此独立,都不知道对方的实现
package main
?
import ("io")
?
type Socket struct{
}
?
func (s *Socket) Write(p []byte) (n int, err error) {
return 0, nil
}
?
func (s *Socket) Close() error {
return nil
}
?
// io 包接口
type Write interface {
Write(p []byte) (n int, err error)
}
// io 包接口
type Closer interface{
Close() error
}
?
func usingWriter(writer io.Writer) {
writer.Write(nil)
}
?
func usingCloser(closer io.Closer) {
closer.Close()
}
?
func main() {
//实例化Socket
s := new(Socket)
usingWriter(s)
usingCloser(s)
}
接口的方法不一定需要一个类型完全实现
接口的方法可以通过在类型中嵌入其他类型或结构体来实现
不需要知道接口的方法是通过一个类型完全实现,还是通过多个结构嵌入到一个结构体中拼凑共同实现
例子:
package main
import(
"fmt"
)
//一个服务器需要满足开启和写日志的功能
type Service interface{
Start()
Log(string)
}
//-------------------------------------
// 日志器
type Logger struct{
}
//实现Service的Log方法
func (g *Logger) Log(l string){
fmt.Printf("打印游戏日志:%s\n", l);
}
//-------------------------------------
//游戏服务
type GameService struct{
//嵌套Logger日志器
Logger
}
//实现Service Start()方法
func (g *GameService) Start() {
fmt.Println("游戏服务开始!!")
}
?
func main() {
var s Service = new(GameService)
s.Start()
s.Log("hello")
}
??
//游戏服务开始!!
//打印游戏日志:hello
日志对外接口
文件写入器
命令行写入器
使用日志
1.日志对外接口 Logger.go
package main
?
// 声明日志写入器接口
type LogWriter interface{
Write(data interface{}) error
}
?
type Logger struct{
// 日志器用到日志写入器
writeList []LogWriter
}
// 注册日志写入器
func (l *Logger) RegisterWriter(writer LogWriter) {
l.writeList = append(l.writeList, writer)
}
//讲一个data类型到数据写入到日志中
func (l *Logger) Log(data interface{}) {
// 遍历所有组册到写入器
for _,writer := range l.writeList {
writer.Write(data)
}
}
?
//创建日期器到实例
func NewLogger() *Logger {
return &Logger{}
}
文件写入器 file.go
package main
?
import (
"fmt"
"errors"
"os"
)
// 声明文件写入器
type fileWriter struct{
file *os.File
}
// 设置文件写入器写入文件名
func (f *fileWriter) SetFile(filename string) (err error) {
//如果文件已打开,关闭前一个文件
if f.file != nil {
f.file.Close()
}
// 创建一个文件并保存文件句柄
f.file, err = os.Create(filename)
// 如果创建到过程出现错误,则返回错误
return err
}
// 实现LogWriter到Write()方法
func (f *fileWriter) Write(data interface{}) error {
//日志文件可能没有创建成功
if f.file == nil {
//日志文件没有准备好
return errors.New("file not created")
}
//将数据序列化为字符串
str := fmt.Sprintf("%v\n", data)
?
//将数据以字节数组写入文件中
_, err := f.file.Write([]byte(str))
?
return err
}
?
// 创建文件写入器实例
func newFileWriter() *fileWriter{
return &fileWriter{}
}
命令行写入器
package main
?
import(
"fmt"
"os"
)
?
//命令行写入器
type consoleWriter struct{}
?
// 实现LogWriter到Write()方法
func (f *consoleWriter) Write(data interface{}) error {
// 将数据序列化为字符串
str := fmt.Sprintf("%v\n", data)
// 将数据以字节数组写入命令行
_, err := os.Stdout.Write([]byte(str))
?
return err
}
// 创建命令写入实例
func newConsoleWriter() *consoleWriter {
return &consoleWriter{}
}
4. 使用日志
package main
import (
"fmt"
)
//创建日志器
func creatLogger() *Logger{
//创建日志器
l := NewLogger()
//创建命令行写入器
cw := newConsoleWriter()
//注册命令行写入器到日志器中
l.RegisterWriter(cw)
//创建文件写入器
fw := newFileWriter()
//设置文件名
if err := fw.SetFile("log.log"); err != nil {
fmt.Println(err)
}
//注册文件写入器到日志器中
l.RegisterWriter(fw)
return l
}
func main() {
//准备日志器
l := creatLogger()
//写一个日志
l.Log("test log")
}
接口定义排序格式:
type Interface interface {
//获取元素数量
Len() int
// 小于比较, 提供i,j两个为元素索引
Less(i,j int) bool
// 交换元素
Swap(i,j int)
}
package main
?
import (
"fmt"
"sort"
)
?
// 将[]string定义为MyStringList类型
type MyStringList []string
// 获取元素数据量
func (m MyStringList) Len() int {
return len(m)
}
// 比较元素
func (m MyStringList) Less(i, j int) bool {
return m[i] < m[j]
}
// 交换元素
func (m MyStringList) Swap(i, j int) {
m[i], m[j] = m[j], m[i]
}
?
func main() {
//准备一个内容被打乱顺序的字符串切片
names := MyStringList{
"3. Triple Kill",
"5. Penta Kill",
"2. Double Kill",
"4. Quadra Kill",
"1. First Kill",
}
// 使用sort包排序
sort.Sort(names)
// 遍历打印
for _, v := range names {
fmt.Printf("%s\n", v)
}
}
结果:
First Kill
Double Kill
Triple Kill
Quadra Kill
Penta Kill
字符串切片
package main
?
import(
"fmt"
"sort"
)
type StringSlice []string
func (p StringSlice) Len() int {
return len(p)
}
func (p StringSlice) Less(i, j int) bool {
return p[i] < p[j]
}
func (p StringSlice) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
?
func main() {
//准备一个内容被打乱顺序的字符串切片
names := sort.StringSlice{
"3. Triple Kill",
"5. Penta Kill",
"2. Double Kill",
"4. Quadra Kill",
"1. First Kill",
}
// 使用sort包排序
sort.Sort(names)
// 遍历打印
for _, v := range names {
fmt.Printf("%s\n", v)
}
}
整形切片排序
package main
?
import(
"fmt"
"sort"
)
type IntSlice []string
?
func (p IntSlice) Len() int {
return len(p)
}
func (p IntSlice) Less(i, j int) bool {
return p[i] < p[j]
}
func (p IntSlice) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
?
func main() {
//准备一个内容被打乱顺序的字符串切片
names := []string{
"3. Triple Kill",
"5. Penta Kill",
"2. Double Kill",
"4. Quadra Kill",
"1. First Kill",
}
// 使用sort包排序
sort.Strings(names)
// 遍历打印
for _, v := range names {
fmt.Printf("%s\n", v)
}
}
sort包内建的类型排序接口
| 类型 | 实现sort.Interface的类型 | 直接排序方法 | 说明 |
|---|---|---|---|
| 字符串(String) | StringSlice | Sort.Strings(a []string) | 字符ASCII升序 |
| 整形(Integer) | IntSlice | Sort.Ints(a []int) | 数值升序 |
| 双精度浮点(float64) | Float64Slice | Sort.Float64s(a []float64) | 数值升序 |
使用sort.Interface进行结构体排序
package main
import(
"fmt"
"sort"
)
// 声明英雄分类
type HeroKind int
// 定义HeroKind常量,类似枚举
const (
None HeroKind = iota
Tank
Assassin
Mage
)
// 定义英雄名单结构
type Hero struct {
Name string //名字
Kind HeroKind //种类
}
//将英雄指针的切片定义为Heros类型
type Heros []*Hero
// 实现sort.Interface接口获取元素数量方法
func (s Heros) Len() int {
return len(s)
}
// 实现sort.Interface接口获比较取元素方法
func (s Heros) Less(i, j int) bool {
// 英雄分类不一致,优先对分类排序
if s[i].Kind != s[j].Kind {
return s[i].Kind < s[j].Kind
}
return s[i].Name < s[j].Name
}
?
func (s Heros) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
?
func main() {
//英雄列表
heros := Heros{
&Hero{"吕布", Tank},
&Hero{"李白", Assassin},
&Hero{"妲己", Mage},
&Hero{"貂蝉", Assassin},
&Hero{"关羽", Tank},
&Hero{"诸葛亮", Mage},
}
// 使用sort包排序
sort.Sort(heros)
// 遍历打印
for _, v := range heros {
fmt.Printf("%+v\n", v)
}
}
结果:
&{Name:关羽 Kind:1} &{Name:吕布 Kind:1} &{Name:李白 Kind:2} &{Name:貂蝉 Kind:2} &{Name:妲己 Kind:3} &{Name:诸葛亮 Kind:3}
使用sort.Slice 进行切片元素排序
Go语言 sort 包提供sort.Slice()函数进行切片排序
定义:
func Slice(slice interface{}, less func{i, j int} bool)
package main
?
import(
"fmt"
"sort"
)
// 声明英雄分类
type HeroKind int
// 定义HeroKind常量,类似枚举
const (
None = iota
Tank
Assassin
Mage
)
// 定义英雄名单结构
type Hero struct {
Name string //名字
Kind HeroKind //种类
}
?
func main() {
//英雄列表
heros := []*Hero{
{"吕布", Tank},
{"李白", Assassin},
{"妲己", Mage},
{"貂蝉", Assassin},
{"关羽", Tank},
{"诸葛亮", Mage},
}
// 使用sort包排序
sort.Slice(heros, func (i, j int) bool {
if heros[i].Kind != heros[j].Kind {
return heros[i].Kind < heros[j].Kind
}
return heros[i].Name != heros[j].Name
})
// 遍历打印
for _, v := range heros {
fmt.Printf("%+v\n", v)
}
}
结果:
&{Name:关羽 Kind:1} &{Name:吕布 Kind:1} &{Name:貂蝉 Kind:2} &{Name:李白 Kind:2} &{Name:诸葛亮 Kind:3} &{Name:妲己 Kind:3}
接口与接口嵌套组合成新接口
只有接口所有方法被实现,则这个接口的所有嵌套接口的方法均可以被调用
系统包中的接口嵌套组合
go语言中i o包定义了写入器(Writer)\关闭器(Closer)和写入关闭器(WriteCloser)3个接口
type Write interface{
Write(p []byte) (n int, err error)
}
?
type Closer interface{
Close() error
}
type WriteCloser interface{
Writer
Closer
}
代码中使用接口嵌套组合
package main
?
import("io")
?
// 声明一个设备结构
type device struct{}
?
// 实现io.Writer()方法
func (d *device) Write(p []byte) (n int, err error ) {
return 0, nil
}
?
// 实现io.Closer Close()方法
func (d *device) Close() error {
return nil
}
?
func main() {
// 声明写入关联器,并赋予device的实例
var wc io.WriteCloser = new(device)
// 写入数据
wc.Write(nil)
// 关闭数据
wc.Close()
// 声明写入器,并赋予device的实例
var writeOnly io.Writer = new (device)
// 写入数据
writeOnly.Write(nil)
}
var wc io.WriteCloser = new(device) 由于 device 实现了io.WriteCloser()方法,所有device指针会被隐式转为io.WriteCloser()接口
GO语言使用接口断言(type asseritions)将接口转为另一接口、另外的类型
类型断言格式:
t := i.(T)
i 接口变量
T 代表转换的目标类型
t代表转换后的变量
注意:如果i没有完全实现T接口方法、会导致宕机 ,可以改成下面写法:
t, ok := i.(T)
鸟和猪各种实现飞行动物、行走动物接口例子:
package main
import "fmt"
// 定义飞行动物接口
type Flyer interface{
Fly()
}
// 定义行走动物接口
type Walker interface{
Walk()
}
// 定义鸟类
type bird struct{
}
// 鸟类实现飞行接口
func (b *bird) Fly() {
fmt.Println("bird: fly")
}
// 定义猪类
type pig struct{
}
// 鸟类实现飞行接口
func (p *pig) Walk() {
fmt.Println("pig: walk")
}
func main() {
// 创建动物的名字到实例化映射
animals := map[string]interface{}{
"bird": new(bird),
"pig": new(pig),
}
// 遍历映射
for name, obj := range animals {
// 判断对象是否飞行动物
f, isFlyer := obj.(Flyer)
// 判断对象是否行走动物
w, isWalker := obj.(Walker)
fmt.Printf("name: %s isFlyer: %v isWalker: %v\n", name, isFlyer, isWalker)
// 飞行动物调用飞行动物接口
if isFlyer {
f.Fly()
}
// 飞行动物调用飞行动物接口
if isWalker {
w.Walk()
}
}
}
结果: name: bird isFlyer: true isWalker: false bird: fly name: pig isFlyer: false isWalker: true pig: walk
// 由于pig 实Walker接口,所有被隐式转换为Walker接口类型
p1 := new(pig)
var a Walker = p1
p2 := a.(*pig)
p3 := a
fmt.Printf("p1=%p p2=%p p3=%p\n", p1, p2, p3)
//p1=0x118ffd0 p2=0x118ffd0 p3=0x118ffd0
空接口是一种非常灵活的数据抽象和使用方法 任何值都能满足接口的需求
package main
import "fmt"
func main() {
var any interface{}
any = 1
fmt.Println(any)
any = "hello"
fmt.Println(any)
any = false
fmt.Println(any)
}
结果: 1 hello false
保存到空接口的值,如果直接取出指定类型的值时,会发生变异错误
// 声明i变量
var a int = 1
var i interface{} = a
var b int = i
fmt.Println(b)
//command-line-arguments
//seven/7.8/nullInterface.go:20:6: cannot use i (type interface {}) as type int in //assignment: need type assertion
// 声明i变量
var a int = 1
var i interface{} = a
// 需要将i 进行断言
var b int = i.(int)
fmt.Println(b)
类型不同的空接口间的比较结果不相同
不能比较空接口中的动态值
类型不同的空接口间的比较结果不相同
// 不同类型空接口结果比较
var a1 interface{} = 100
var b1 interface{} = "hi"
fmt.Println(a1 == b1)
// false
不能比较空接口中的动态值
// 不能比较空接口的动态值
var c interface{} = []int{10}
var d interface{} = []int{20}
fmt.Println(c == d)
//panic: runtime error: comparing uncomparable type []int
类型的可可比性
| 类型 | 说明 |
|---|---|
| map | 宕机 |
| 切片([]T) | 宕机 |
| 通道(channel) | 可比较 |
| 通道(channel) | 可比较 |
| 结构体 | 可比较 |
| 函数 | 可比较 |
值设置和获取
遍历字段的所有键值关联数据
初始化和清除
使用字典
dict.go
package main
?
import "fmt"
?
// 1.设置值和获取
type Dictionary struct{
data map[interface{}]interface{}
}
// 根据键获取值
func (d *Dictionary) Get(key interface{}) interface{} {
return d.data[key]
}
// 设置键值
func (d *Dictionary) Set(key interface{}, value interface{}) {
d.data[key] = value
}
?
// 2.遍历字段的所有键值关联数据
func (d *Dictionary) Visit(callback func(k, v interface{}) bool) {
if callback == nil {
return
}
for k, v := range d.data {
if !callback(k, v) {
return
}
}
}
?
// 3.初始化和清除
func (d *Dictionary) Clear() {
d.data = make(map[interface{}]interface{})
}
// 创建一个字典
func NewDictionary() *Dictionary {
d := &Dictionary{}
//初识化
d.Clear()
return d
}
?
func main() {
// 创建字典
dict := NewDictionary()
// 添加数据
dict.Set("My Factory", 60)
dict.Set("Terra craft", 30)
dict.Set("Don`t Hugry", 23)
?
// 获取打印
favorite := dict.Get("My Factory")
fmt.Println("favorite:", favorite)
?
// 遍历所有字段元素
dict.Visit(func(key, value interface{})bool {
// 将值转int类型 判断 大于 40
if value.(int) > 40 {
fmt.Println(key, "is expensive")
return true
}
fmt.Println(key, "is cheap")
return true
})
}
?
结果: favorite: 60 Terra craft is cheap Don`t Hugry is cheap My Factory is expensive
go语言switch 特殊用途:判断一个接口内保存 或实现的类型
switch 接口变量.(type) {
case 类型1:
//变量类型1时处理
case 类型2:
//变量类型2时处理
default:
//变量默认处理
}
package main
import "fmt"
?
// 打印类型
func printType(v interface{}) {
switch v.(type) {
case int:
fmt.Println(v, "v is int")
case string:
fmt.Println(v, "v is string")
case bool:
fmt.Println(v, "v is bool")
}
}
func main() {
printType(1024)
printType("hello go")
printType(true)
}
结果: 1024 v is int hello go v is string true v is bool
package main
import "fmt"
// 定义电子支付方式
type Alipay struct{}
?
// alipay添加支持刷脸
func (a *Alipay) CanUseFaceID() {
}
//定义现金方式
type Cash struct{}
//添加被偷窃方法
func (c *Cash) Stolen() {
}
// 具备刷脸接口
type CantainCanUseFaceID interface{
CanUseFaceID()
}
// 具备被偷的接口
type ContainStolen interface{
Stolen()
}
//打印支付方式特点
func print(payMethod interface{}) {
switch payMethod.(type) {
case CantainCanUseFaceID:
fmt.Printf("%T can use faceid\n", payMethod)
case ContainStolen:
fmt.Printf("%T may be stolen\n", payMethod)
}
}
func main() {
//使用电子支付判断
print(&Alipay{})
//使用现金支付
print(&Cash{})
}
结果: *main.Alipay can use faceid *main.Cash may be stolen
有限状态机(Finite-Sate Machine, FSM)表示有限个状态及在这些状态间的转移和行为的数学模型
例子目的:实现状态接口、状态管理器及一系列的状态和使用状态的逻辑
自定义状态需求实现的接口
状态信息
状态管理
在状态间转移
自定义状态实现状态接口
seven/7.11/fsm/state.go
package main
import (
"reflect"
)
// 状态接口
type State interface {
// 状态名字
Name() string
// 是否允许通状态转移
EnableSameTransit() bool
// 响应状态开始
OnBegin()
// 响应状态结束
OnEnd()
// 判断能否转移到某个状态
CanTransitTo(name string) bool
}
?
// 从状态实例获取状态名
func StateName(s State) string {
if s == nil {
return "none"
}
// 使用反射获取状态到名称
return reflect.TypeOf(s).Elem().Name()
}
seven/7.11/fsm/info.go
package main
?
// 状态基本信息和默认实现
type StateInfo struct {
// 状态名
name string
}
// 状态名
func (s *StateInfo) Name() string {
return s.name
}
// 提供内部设置名字
func (s *StateInfo) setName(name string) {
s.name = name
}
// 允许同状态转移
func (s *StateInfo) EnableSameTransit() bool {
return false
}
// 默认状态开启实现
func (s *StateInfo) OnBegin() {
}
// 状态结束
func (s *StateInfo) OnEnd() {
}
func (s *StateInfo) CanTransitTo(name string) bool {
return true
}
3.seven/7.11/fsm/statemgr.go
package main
import "errors"
// 状态管理器
type StateManager struct {
// 已经添加到状态
stateByName map[string]State
// 状态改变时回调
OnChange func(from, to State)
// 当前状态
curr State
}
// 添加一个状态到管理器中
func (sm *StateManager) Add(s State) {
// 获取状态名称
name := StateName(s)
// 将s转为能设置名字接口,并调用该接口
s.(interface {
setName(name string)
}).setName(name)
//根据状态名获取已经添加到状态,检查该状态是否存在
if sm.Get(name) != nil {
panic("duplicate state:" + name)
}
//根据名字保存到map中
sm.stateByName[name] = s
}
?
// 根据名字获取指定状态
func (sm *StateManager) Get(name string) State {
if v, ok := sm.stateByName[name]; ok {
return v
}
return nil
}
?
// 初始化状态管理
func NewStateManager() *StateManager {
return &StateManager{
stateByName: make(map[string]State),
}
}
?
// 状态没有找到到错误
var ErrStateNotFound = errors.New("state no found")
// 禁止在同状态转移
var ErrForbidSameStateStransit = errors.New("forbid same state transit")
// 不能转移到指定状态
var ErrCannotTransitToState = errors.New("cannot transit to state")
?
// 获取当前到状态
func (sm *StateManager) CurrState() State {
return sm.curr
}
?
// 当前状态能否转转移到目标状态
func (sm *StateManager) CanCurrTransitTo(name string) bool {
if sm.curr == nil {
return true
}
// 相同状态
if sm.curr.Name() == name && !sm.curr.EnableSameTransit() {
return false
}
// 使用当前状态,检查能否转移到指定名字到状态
return sm.curr.CanTransitTo(name)
}
?
// 转移到指定状态
func (sm *StateManager) Transit(name string) error {
// 获取目标状态
next := sm.Get(name)
// 目标不存在
if next == nil {
return ErrStateNotFound
}
// 记录转移前到状态
pre := sm.curr
// 当前状态
if sm.curr != nil {
// 相同状态不能转移
if sm.curr.Name() == name && !sm.curr.EnableSameTransit() {
return ErrForbidSameStateStransit
}
// 不能转移到目标状态
if !sm.curr.CanTransitTo(name) {
return ErrCannotTransitToState
}
// 结束状态
sm.curr.OnEnd()
}
// 当前状体切换到要转移目标状态
sm.curr = next
// 状态新开始
sm.curr.OnBegin()
// 通知回调
if sm.OnChange != nil {
sm.OnChange(pre, sm.curr)
}
return nil
}
seven/7.11/fsm/main.go
package main
import (
"fmt"
)
//闲置状态
type IdleState struct {
StateInfo //使用SateInfo实现基础接口
}
// 重新实现状态开发
func (i *IdleState) OnBegin() {
fmt.Println("IdleState begin")
}
// 重新实现状态结束
func (i *IdleState) OnEnd() {
fmt.Println("IdleState end")
}
?
//移动状态
type MoveState struct {
StateInfo //使用SateInfo实现基础接口
}
// 重新实现状态开发
func (m *MoveState) OnBegin() {
fmt.Println("MoveState begin")
}
func (m *MoveState) EnableSameTransit() bool {
return true
}
?
//跳跃状态
type JumpState struct {
StateInfo //使用SateInfo实现基础接口
}
// 重新实现状态开发
func (j *JumpState) OnBegin() {
fmt.Println("JumpSate begin")
}
// 跳跃状态不能转移到移动状态
func (j *JumpState) CanTransitTo(name string) bool {
return name != "MoveState"
}
?
func main() {
// 实例化一个状态管理器
sm := NewStateManager()
// 响应状态转移通知
sm.OnChange = func (from, to State) {
// 打印状态转移去向
fmt.Printf("%s ---> %s \n", StateName(from), StateName(to))
}
?
// 添加3个状态
sm.Add(new(IdleState))
sm.Add(new(MoveState))
sm.Add(new(JumpState))
?
// 在不同状态间转移
transitAndReport(sm, "IdleState")
transitAndReport(sm, "MoveState")
transitAndReport(sm, "MoveState")
transitAndReport(sm, "JumpState")
transitAndReport(sm, "JumpState")
transitAndReport(sm, "IdleState")
?
}
?
// 封装转移状态和输出日志
func transitAndReport(sm *StateManager, target string) {
if err := sm.Transit(target); err != nil {
fmt.Printf("FAILED! %s --> %s, %s\n\n", sm.CurrState().Name(), target, err.Error())
}
}
结果:
IdleState begin none ---> IdleState IdleState end MoveState begin IdleState ---> MoveState MoveState begin MoveState ---> MoveState JumpSate begin MoveState ---> JumpState FAILED! JumpState --> JumpState, forbid same state transit
IdleState begin
原文:https://www.cnblogs.com/smallyi/p/12543401.html