Go语言的接口

GO写web接口(采集mysql数据向前端传json) 安装两个包 打开控制台 //安装web接口包 go get github.com/gin-gonic/gin //安装mysql操作包 go get github.com/go-sql-driver/mysql 引入需要支持的包 geymysql.go import ( //引入mysql的处理 "database/sql" _"github.com/go-sql-driver/mysql" "fmt" //引入json格式化 "encoding/json" 阅读详情

 使用接口:

接口声明的格式形式代码如下:

type 接口类型名 interface{

        方法名1 ( 参数列表1 ) 返回值列表1

        方法名2 ( 参数列表2 ) 返回值列表2

        ……

}

接口类型名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer,有关闭功能的接口叫Closer等。

方法名:当方法名首字母是大写时,且这个接口类型名首字母也是大写时,这个方法可以被接口所有的包(package)之外的代码访问。

参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以被忽略。

goroutine和channel是支撑起Go语言的并发模型的基石。

格式定义接口,如下:

type Namer interface{

        Method1(param_list) return_type

        Method2(param_list) return_type

        ...

}

Namer是一个接口类型。

使用接口Robot,程序清单如下:

package main

import(
  "fmt"
  "errors"
)
type Robot interface{
  PowerOn() error
}
type T850 struct{
  Name string
}
func(a *T850)PowerOn()error{
  return nil
}
type R2D2 struct{
  Broken bool
}
func (r *R2D2)PowerOn()error{
  if r.Broken{
    return errors.New("R2D2 is broken")
  }else{
    return nil
  }
}
func Boot(r Robot)error{
  return r.PowerOn()
}
func main(){
  t:=T850{
    Name:"The Terminator",
  }
  r:=R2D2{
    Broken:true,
  }
  err:=Boot(&r)
  if err!=nil{
    fmt.Println(err)
  }else{
    fmt.Println("Robot is powered on!")
  }
  err=Boot(&t)
  if err!=nil{
    fmt.Println(err)
  }else{
    fmt.Println("Robot is powered on!")
  }
}

运行结果如下:

R2D2 is broken
Robot is powered on!

代码如下:

package main

import (
	"fmt"
)

type Shaper interface {
	Area() float32
}

type Square struct {
	side float32
}

func (sq *Square) Area() float32 {
	return sq.side * sq.side
}

func main() {
	sq1 := new(Square)
	sq1.side = 5
	areaIntf := sq1
	fmt.Printf("面积为: %f\n", areaIntf.Area())
}

运行结果如下:

面积为: 25.000000

代码如下:

package main

import (
	"fmt"
)

type Shaper interface {
	Area() float32
}

type Square struct {
	side float32
}

func (sq *Square) Area() float32 {
	return sq.side * sq.side
}

type Rectangle struct {
	length, width float32
}

func (r Rectangle) Area() float32 {
	return r.length * r.width
}

func main() {
	r := Rectangle{5, 3}
	q := &Square{5}
	shapes := []Shaper{r, q}
	for n, _ := range shapes {
		fmt.Println("形状参数:", shapes[n])
		fmt.Println("形状面积是:", shapes[n].Area())
	}
}

运行结果如下:

形状参数: {5 3}
形状面积是: 15
形状参数: &{5}
形状面积是: 25

代码如下:

package main

import (
	"fmt"
)

type stockPosition struct {
	ticker     string
	sharePrice float32
	count      float32
}

func (s stockPosition) getValue() float32 {
	return s.sharePrice * s.count
}

type car struct {
	make  string
	model string
	price float32
}

func (c car) getValue() float32 {
	return c.price
}

type valuable interface {
	getValue() float32
}

func showValue(asset valuable) {
	fmt.Printf("资产的价值是: %f\n", asset.getValue())
}

func main() {
	var o valuable = stockPosition{"GOOG", 577.20, 4}
	showValue(o)
	o = car{"BMW", "M3", 66500}
	showValue(o)
}

运行结果如下:

资产的价值是: 2308.800049
资产的价值是: 66500.000000

接口类型和约定

1.动态类型

代码如下:(没看懂)

package main

import (
	"fmt"
	"math"
)

type Square struct {
	side float32
}

type Circle struct {
	radius float32
}

type Shaper interface {
	Area() float32
}

func (sq *Square) Area() float32 {
	return sq.side * sq.side
}

func (ci *Circle) Area() float32 {
	return ci.radius * ci.radius * math.Pi
}

func main() {
	var areaIntf Shaper
	sq1 := new(Square)
	sq1.side = 5

	areaIntf = sq1
	if t, ok := areaIntf.(*Square); ok {
		fmt.Printf("areaIntf的类型是:%T\n", t)
	}
	if u, ok := areaIntf.(*Circle); ok {
		fmt.Printf("areaIntf的类型是:%T\n", u)
	} else {
		fmt.Println("areaIntf不含类型为Circle的变量")
	}
}

运行结果如下:

areaIntf的类型是:*main.Square
areaIntf不含类型为Circle的变量

2.类型判断

接口实现

嵌套接口

接口赋值

接口查询

接口组合

实现接口的条件:

接口被实现的条件一:接口的方法与实现接口的类型方法格式一致

代码如下:

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")
}

运行结果如下:

WriteData: data

条件二:接口中所有方法均被发现

理解类型与接口的关系

一、一个类型可以实现多个接口

二、多个类型可以实现相同的接口

示例:便于扩展输出方式的日志系统

1、日志对外接口

日志写入器,logger.go的代码如下:

package main

//声明日志写入器接口
type LogWriter interface {
	Write(data interface{}) error
}

//日志器
type Logger struct {
	//这个日志器用到的日志写入器
	writerList []LogWriter
}

//注册一个日志写入器
func (l *Logger) RegisterWriter(writer LogWriter) {
	l.writerList = append(l.writerList, writer)
}

//将一个data类型的数据写入日志
func (l *Logger) Log(data interface{}) {
	//遍历所有注册的写入器
	for _, writer := range l.writerList {
		//将日志输出到每一个写入器中
		writer.Write(data)
	}
}

//创建日志器的实例
func NewLogger() *Logger {
	return &Logger{}
}

2、文件写入器

file.go的代码如下:

package main

import (
	"errors"
	"fmt"
	"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{}
}

3、命令行写入器

console.go的代码如下:

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、使用日志

main.go的代码如下:

package main

import "fmt"

func createLogger() *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 1
}

func main() {
	//准备日志器
	l := createLogger()
	//写一个日志
	l.Log("Hello")
}

示例:使用接口进行数据的排序

一、使用sort.Interface接口进行排序

sortstring.go,字符串排序,代码如下:

package main

import (
	"fmt"
	"sort"
)

//将[]string定义为MyStringList类型
type MyStringList []string

//实现sort.Interface接口的获取元素数量方法
func (m MyStringList) Len() int {
	return len(m)
}

//实现sort.Interface接口的比较元素方法
func (m MyStringList) Less(i, j int) bool {
	return m[i] < m[j]
}

//实现sort。Interface接口的交换元素方法
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 Blood",
	}

	//使用sort包进行排序
	sort.Sort(names)

	//遍历打印结果
	for _, v := range names {
		fmt.Printf("%s\n", v)
	}
}

运行结果如下:

1. First Blood
2. Double Kill
3. Triple Kill
4. Quadra Kill
5. Penta Kill

二、常见类型的便捷排序

1、字符串切片的便捷排序

2、对整形切片进行排序

3、sort包内建的类型排序接口一览

sort包中内建的类型排序接口
类型实现sort.Interface的类型直接排序方法说明
字符口中(String)StringSlicesort.Strings(a []string)字符ASCII值升序
整形(int)IntSlicesort.Ints(a []int)数值升序
双精度浮点(float64)Float64Slicesort.Float64s(a []float64)数值升序

三、对结构体数据进行排序

1、完整实现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
}

//实现sort.Interface接口交换元素方法
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}

2、使用sort.Slice进行切片元素排序

代码如下:

package main

import (
	"fmt"
	"sort"
)

//声明英雄的分类
type HeroKind int

//定义HeroKind常量,类似于枚举
const (
	None HeroKind = iota
	Tank
	Assassin
	Mage
)

//定义英雄名单的结构
type Hero struct {
	Name string   //英雄的名字
	Kind HeroKind //英雄的种类
}

func main() {

	//准备英雄列表
	heros := []*Hero{
		&Hero{"吕布", Tank},
		&Hero{"李白", Assassin},
		&Hero{"妲己", Mage},
		&Hero{"貂蝉", Assassin},
		&Hero{"关羽", Tank},
		&Hero{"诸葛亮", Mage},
	}

	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}

接口的嵌套组合——将多个接口放在一个接口内

在接口和类型间转换

一、类型断言的格式

类型断言的基本格式如下:

t:=i.(T)

i代表接口变量

T代表转换的目标类型

t代表转换后的变量

二、将接口转换为其他接口

鸟与猪,代码如下:

package main

import (
	"fmt"
)

//定义飞行动物接口
type Flyer interface {
	Fly()
}

//定义行走动物接口
type Walker interface {
	Walk()
}

//定义鸟类
type bird struct{}

//实现飞行动物接口
func (b *bird) Fly() {
	fmt.Println("bird: fly")
}

//为鸟添加Walk()方法,实现行走动物接口
func (b *bird) Walk() {
	fmt.Println("bird: walk")
}

//定义猪
type pig struct{}

//为猪添加Walk()方法,实现行走动物接口
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: true
bird: fly
bird: walk
name: pig isFlyer:false isWalker: true
pig: walk

书上的运行结果如下:

name: pig isFlyer:false isWalker: true
pig: walk
name: bird isFlyer:true isWalker: true
bird: fly
bird: walk

三、将接口转换为其它类型

空接口类型()——能保存所有值的类型

一、将值保存到空接口

代码如下:

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

二、从空接口获取值

代码如下:

package main

import (
	"fmt"
)

func main() {
	//声明a变量,类型int,初始值为1
	var a int = 1
	//声明i变量,类型为interface{},初始值为a,此时i的值变为1
	var i interface{} = a
	//声明b变量,尝试赋值i
	var b int = i.(int)
	fmt.Println(b)
}

运行结果如下:

1

三、空接口的值比较

1、类型不同的空接口间的比较结果不相同

代码如下:

package main

import (
	"fmt"
)

func main() {
	//a保存整形
	var a interface{} = 100
	//b保存字符串
	var b interface{} = "hi"
	//两个空接口不相等
	fmt.Println(a == b)
}

运行结果如下:

false

2、不能比较空接口中的动态值

类型的可比较性
类型说明
map宕机错误,不可比较
切片([]T)宕机错误,不可比较
通道(channel)可比较,必须由同一个make生成,也就是同一个通道才会是true,否则为false
数组([容量]T)可比较,编译期知道两个数组是否一致
结构体可比较,可以逐个比较结构体的值
函数可比较

示例:使用空接口实现可以保存任意值的字典

代码如下:

package main

import (
	"fmt"
)

//字典结构
type Dictionary struct {
	data map[interface{}]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
}

//遍历所有的键值,如果回调返回值为false,停止遍历
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
		}
	}
}

//清空所有的数据
func (d *Dictionary) Clear() {
	d.data = make(map[interface{}]interface{})
}

//创建一个字典
func NewDictionary() *Dictionary {
	d := &Dictionary{}
	//初始化map
	d.Clear()
	return d
}

func main() {
	//创建字典实例
	dict := NewDictionary()

	//添加游戏数据
	dict.Set("My Factory", 60)
	dict.Set("Terra Craft", 36)
	dict.Set("Don't Hungry", 24)

	//获取值及打印值
	favorite := dict.Get("Terra Craft")
	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: 36
My Factory is expensive
Terra Craft is cheap
Don't Hungry is cheap

类型分支——批量判断空接口中变量的类型

一、类型断言的书写格式

switch实现类型分支时的写法格式如下:

switch 接口变量.(type){

        case 类型1:

                //变量是类型1时的处理

        case 类型2:

                //变量是类型2时的处理

……

default:

                //变量不是所有case中列举的类型时的处理

}

二、使用类型分支判断基本类型

代码如下:

package main

import (
	"fmt"
)

func printType(v interface{}) {
	switch v.(type) {
	case int:
		fmt.Println(v, "is int")
	case string:
		fmt.Println(v, "is string")
	case bool:
		fmt.Println(v, "is bool")
	}
}

func main() {
	printType(1024)
	printType("pig")
	printType(true)
}

运行结果如下:

1024 is int
pig is string
true is bool

三、使用类型分支判断接口类型

代码如下:

package main

import (
	"fmt"
)

//电子支付方式
type Alipay struct {
}

//为Alipay添加CanUseFaceID()方法,表示电子支付方式支持刷脸
func (a *Alipay) CanUseFaceID() {

}

//现金支付方式
type Cash struct {
}

//Cash添加Stolen()方式,表示现金支付方式会出现 偷窥情况
func (a *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(new(Alipay))

	//使用现金判断
	print((new(Cash)))
}

运行结果如下:

*main.Alipay can use faceid
*main.Cash may be stolen

示例:实现有限状态机(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()
}

三、状态基本信息

状态信息,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
}

四、状态管理

状态管理器,statemgr.go,代码如下:

package main

import "errors"

// 状态没有找到的错误
var ErrStateNotFound = errors.New("state not found")

// 禁止在同状态间转移
var ErrForbidSameStateTransit = errors.New("forbid same state transit")

// 不能转移到指定状态
var ErrCannotTransitToState = errors.New("cannot transit to state")

// 状态管理器
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 (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 ErrForbidSameStateTransit
		}

		// 不能转移到目标状态
		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
}

// 初始化状态管理器
func NewStateManager() *StateManager {
	return &StateManager{
		stateByName: make(map[string]State),
	}
}

五、在状态间转移

六、自定义状态实现状态接口

一系列状态实现,main.go,代码如下:

package main

import (
	"fmt"
)

// 闲置状态
type IdleState struct {
	StateInfo // 使用StateInfo实现基础接口
}

// 重新实现状态开始
func (i *IdleState) OnBegin() {
	fmt.Println("IdleState begin")
}

// 重新实现状态结束
func (i *IdleState) OnEnd() {
	fmt.Println("IdleState end")
}

// 移动状态
type MoveState struct {
	StateInfo
}

func (m *MoveState) OnBegin() {
	fmt.Println("MoveState begin")
}

// 允许移动状态互相转换
func (m *MoveState) EnableSameTransit() bool {
	return true
}

// 跳跃状态
type JumpState struct {
	StateInfo
}

func (j *JumpState) OnBegin() {
	fmt.Println("JumpState begin")
}

// 跳跃状态不能转移到移动状态
func (j *JumpState) CanTransitTo(name string) bool {
	return name != "MoveState"
}

// 封装转移状态和输出日志
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())
	}
}

func main() {
	// 实例化一个状态管理器
	sm := NewStateManager()

	// 响应状态转移的通知
	sm.OnChange = func(from, to State) {

		// 打印状态转移的流向
		fmt.Printf("%s ---> %s\n\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")
}

七、使用状态机

运行结果如下:

PS C:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\Go\fsm> go run main.go
# command-line-arguments
.\main.go:9:2: undefined: StateInfo
.\main.go:24:2: undefined: StateInfo
.\main.go:38:2: undefined: StateInfo
.\main.go:51:27: undefined: StateManager
.\main.go:59:8: undefined: NewStateManager
.\main.go:62:30: undefined: State
.\main.go:65:32: undefined: StateName
PS C:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\Go\fsm>

代码如下:

package main

import (
	"fmt"
)

type A struct {
	Books int
}

type B interface {
	f()
}

func (a A) f() {
	fmt.Println("A.f() ", a.Books)
}

type I int

func (i I) f() {
	fmt.Println("I.f() ", i)
}

func main() {
	var a A = A{Books: 9}
	a.f()

	var b B = A{Books: 99} // 接口类型可接受结构体A的值,因为结构体A实现了接口
	b.f()

	var i I = 199 // I是int类型引申出来的新类型
	i.f()

	var b2 B = I(299) // 接口类型可接受新类型I的值,因为新类型I实现了接口
	b2.f()
}

运行结果如下:

[Running] go run "c:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\Go\main.go"
A.f()  9
A.f()  99
I.f()  199
I.f()  299

[Done] exited with code=0 in 8.338 seconds

代码如下:

package main

import (
	"fmt"
)

type I interface {
	f()
}

type T string

func (t T) f() {
	fmt.Println("T Method")
}

type Stringer interface {
	String() string
}

func main() {

	// 类型断言
	var varI I
	varI = T("Tstring")
	if v, ok := varI.(T); ok { // 类型断言
		fmt.Println("varI类型断言结果为:", v) // varI已经转为T类型
		varI.f()
	}

	// Type-switch做类型判断
	var value interface{} // 默认为零值

	switch str := value.(type) {
	case string:
		fmt.Println("value类型断言结果为string:", str)

	case Stringer:
		fmt.Println("value类型断言结果为Stringer:", str)

	default:
		fmt.Println("value类型不在上述类型之中")
	}

	// Comma-ok断言
	value = "类型断言检查"
	str, ok := value.(string)
	if ok {
		fmt.Printf("value类型断言结果为:%T\n", str) // str已经转为string类型
	} else {
		fmt.Printf("value不是string类型 \n")
	}
}

运行结果如下:

[Running] go run "c:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\Go\main.go"
varI类型断言结果为: Tstring
T Method
value类型不在上述类型之中
value类型断言结果为:string

[Done] exited with code=0 in 12.094 seconds

代码如下:

运行结果如下:

【Golang】Go语言接口与多态 在Go语言中,接口(Interface)是一个重要的特性,它允许我们定义一组方法但不实现它们,任何类型只要实现了这些方法,就被认为是实现了该接口。 接口体现了程序设计的多态、高内聚、低耦合的思想,是实现面向对象编程中多态性的关键工具。 阅读详情

相关推荐

【Golang】Go语言Seeker接口与文件断点续传实战

在现代软件开发中,高效的输入输出(I/O)操作是提高程序性能的关键之一。特别是在处理大量数据时,I/O操作的效率直接影响到应用程序的响应速度和用户体验。Go语言标准库中的io包提供了一系列接口,用于处理各种I/O操作,其中Seeker接口在处理大文件或需要随机访问的场景中非常有用。本文将结合具体案例,详细介绍Go语言中io包的Seeker接口的用法。

景天科技苑 5万+

Go 接口的使用

Go 接口使用

qq_43765199的博客 471

【Go 语言编程实战】用 Golang 实现一个 Go Thrift RPC 接口的泛化调用代码实例

本文详细讲解一下 go rpc 泛化调用的原理和过程和如何使用 golang 实现的 go thrift RPC 接口的泛化调用代码。

AI天才研究院 1万+

GoLang实现接口的方式

GoLang实现接口的方式

Addroc_Sue的博客 2465

抖音直播间人气接口算法 抖音协议

抖音直播间人气接口算法 抖音协议 抖音算法

Q44804487的博客 1万+

STM32H723串口接收全为0 & CAN错误中断问题

STM32H723串口接收全为0 和 叠加的导致CAN错误问题

weixin_38743772的博客 3916

Go语言-【接口】-interface和空接口类型interface{}

在面向对象编程中,可以这么说:“接口定义了对象的行为”,那么具体的实现行为就取决于对象了。 在Go中,接口是一组方法签名(声明的是一组方法的集合)。当一个类型为接口中的所有方法提供定义时,它被称为实现该接口。它与oop非常相似。接口指定类型应具有的方法,类型决定如何实现这些方法。 让我们来看看这个例子:Animal类型是一个接口,我们将定义一个Animal作为任何可以说话的东西。这是 Go 类型系统的核心概念:我们根据类型可以执行的操作而不是其所能容纳的数据类型来设计抽象。 1 ...

xyz的专栏 1544

PyCharm旧版本下载地址

快捷找到PyCharm历史版本下载地址

piaopiaolanghua的专栏 4429

Go语言接口

Go语言接口

YIYIYI 1094

基于Arduino UNO的OLED显示器使用方法x2(Arduino系列五)

Arduino使用OLED显示模块可以实现串口数据显示,还可以与其他模块联合使用,实现实时显示开发板实时数据。

LNN0212的博客 4170

FileZilla Server目录显示失败的处理方法

这两天河南大暴雨挺严重导致多处停电,我远在郑州机房的服务器也断网连不上了。一直到今天下午才恢复,影响较大,有些客户要数据,可是数据因为断网拿不到。于是我想,要是能把服务器的数据备份到其他地方,比如我的本机或者另外一台电脑该多好。 于是今天一整天我都在找远程备份数据的方法,把一台服务器上的数据实时同步到另外一台上。或者两台服务器互相同步。 找了半天发现了FreeFileSync这个宝藏软件,它的原理很简单是通过ftp传输数据。 我得先在服务器上安装个ftp服务,首选filezilla server,安..

两天打鱼三天晒网 4452

【Golang】在Go语言中如何实现接口

接口是Go语言中的一个核心概念,它为类型定义了行为。接口提供了一种方式,使得不同的类型可以被同样对待,只要它们实现了相同的接口。在这篇博客中,我们将讨论如何在Go语言中实现接口。接口是Go语言中的一个强大特性,它提供了一种强大的抽象机制,可以帮助我们编写更加灵活和可扩展的代码。通过定义接口,我们可以定义对象的行为,而不是它们具体的实现。这使得我们的代码更加灵活,更易于测试和维护。希望这篇博客能帮助你理解如何在Go中实现接口。如果你有任何问题或建议,欢迎在评论区留言。

tr6666的博客 1628

【FISCO-BCOS】 八、ubuntu配置Java环境

ubuntu配置Java环境需根据自己的java版本号进行细节修改,本文以jdk11.0.18版本为例。ps:通过此法还可以查看java安装路径,但必须是环境配置成功的情况下才能使用。二、添加Java环境路径。四、检查java环境设置。

naihe_fish的博客 4308

【Golang】go语言面向接口

Go语言的接口是一种抽象数据类型,是一系列接口的集合,接口把所有的具有共性的方法定义在一起,任何其它类型只要实现了接口定义的方法就是实现了接口。接口是duck-type编程的一种体现,不关心属性(数据),只关心行为(方法)。 type 接口类型名 interface{ 方法名1(参数列表)(返回值) 方法名2(参数列表)(返回值) ... } type Fruit i...

smx_dd的博客 506

LabVIEW实现TCP通信

在本节中,我们将简要探讨TCP通信的基础知识,并以工业物联网平台TLINK中的云端设备数据发布设计为例,详细阐述在互联网环境下TCP客户端应用程序设计的基本方法。

不脱发的程序猿 1959

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Golang基础语法(go语言指针、go语言方法、go语言接口、go语言断言)

Go语言指针与方法摘要: 指针是存储变量内存地址的特殊变量,通过*声明指针变量,&获取地址。指针大小取决于系统位数(32位4字节/64位8字节)。创建指针可通过直接声明或new()函数,后者会自动分配内存。 方法是绑定到特定类型的函数,定义时在func和方法名之间添加接收者(值或指针类型)。方法调用通过.操作符完成,支持指针和值类型的隐式转换。Go通过结构体组合实现"继承",子结构体内嵌父结构体即可继承其属性和方法。 接口是一组方法声明的集合,任何实现全部方法的类型即实现了该接口

Atticus的博客 936

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

【Golang 基础系列十六】Go 语言接口

概述 Go 语言中的接口就是方法签名的集合,接口只有声明,没有实现,不包含变量。 语法 定义接口 type [接口名] interface { 方法名1(参数列表) 返回值列表 方法名2(参数列表) 返回值列表 ... } 例子 type Isay interface{ sayHi() } 实现接口 例子 //定义接口的实现类 type Chinese...

郭枫的博客 731

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

第07章 Go语言接口(interface),Golang接口(interface)

接口本身是调用方和实现方均需要遵守的一种协议,大家按照统一的方法命名参数类型和数量来协调逻辑处理的过程。 Go 语言中使用组合实现对象特性的描述。对象的内部使用结构体内嵌组合对象应该具有的特性,对外通过接口暴露能使用的特性。 Go 语言的接口设计是非侵入式的,接口编写者无须知道接口被哪些类型实现。而接口实现者只需知道实现的是什么样子的接口,但无须指明实现哪一个接口。编译器知道最终编译时使用哪个类型实现哪个接口,或者接口应该由谁来实现。 其它编程语言中的接口 接口是一种较为常见的特性,很多语言都有接口特性

心情第一 1141

Go语言自学系列 | golang接口

视频来源:B站《golang入门到项目实战 [2021最新Go语言教程,没有废话,纯干货!持续更新中...]》 一边学习一边整理老师的课程内容及试验笔记,并与大家分享,侵权即删,谢谢支持! 附上汇总贴:Go语言自学系列 | 汇总_COCOgsta的博客-优快云博客 接口像是一个公司里面的领导,他会定义一些通用规范,只设计规范,而不实现规范。 go语言的接口,是一种新的类型定义,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。 语法格式和方法非常类似。 接口

团爸讲算法 413

12 go语言(golang) - 数据类型:接口

在 Go 中,接口通过type关键字进行定义,并且包含一组方法签名。在上面的例子中,JDBCUtil是一个接口,它要求任何实现该接口的类型都必须有和方法。

瑞瑞的博客 1418

【Golang学习之旅】Go 语言错误处理(error 接口、panic、recover)

✅ 适合人群:Golang 开发者 | 后端开发工程师 | 并发编程初学者✅ 文章亮点:深入解析 error、panic、recover + 代码示例 + 实战场景✅ 目标:掌握 Go 语言错误处理机制,编写健壮、高质量的 Go 代码!error🔹 Go 语言错误处理的基本原则:✅ 普通错误使用error处理(推荐方式)✅ 严重错误使用panic处理✅ 程序应尽可能优雅地恢复panic用于表示程序发生严重错误,无法继续执行,通常用于不可恢复的错误,如:✅ 数组越界✅ 空指针引用。

程序员林北北的博客 1278

Go语言基础之接口

文章目录Go语言基础之接口接口接口类型为什么要使用接口接口的定义实现接口的条件接口类型变量值接收者和指针接收者实现接口的区别值接收者实现接口指针接收者实现接口面试题类型与接口的关系一个类型实现多个接口多个类型实现同一接口接口嵌套空接口空接口的定义空接口的应用空接口作为函数的参数空接口作为map的值类型断言接口值 Go语言基础之接口 接口(interface)定义了一个对象的行为规范,只定义规范不实...

Allen 1万+

Golang_08: Go语言 接口 与 面向对象

接口类型 是对其他类型行为的概括和抽象,一个接口类型约定了有零个或多个方法。Go 语言的接口不需要显式实现,一个类型只要拥有一个接口约定的所有方法,那么就表示该类型实现了这个接口。对于一个具体的类型,也不需要声明它实现了哪些接口。接口类型属于引用类型,其零值为空指针 nil。

谢TS的博客 679

Go语言自学系列 | golang接口嵌套

视频来源:B站《golang入门到项目实战 [2021最新Go语言教程,没有废话,纯干货!持续更新中...]》 一边学习一边整理老师的课程内容及试验笔记,并与大家分享,侵权即删,谢谢支持! 附上汇总贴:Go语言自学系列 | 汇总_COCOgsta的博客-优快云博客 接口可以通过嵌套,创建新的接口。例如:飞鱼,既可以飞,又可以游泳。我们创建一个飞Fly接口,创建一个游泳接口Swim,飞鱼接口由这两个接口组成。 飞Fly接口 type Flyer interface { fly() }

团爸讲算法 642

golang 切片 接口_一文搞懂Go语言中的切片排序

本文首发于“Gopher部落”知识星球!切片是Go语言中引入的用于在大多数场合替代数组的语法元素。切片是长度可变的同类型元素序列,它不支持存储不同类型的元素,当然如果你非用sl := []interface{}{"hello", 11, 3.14}来抬杠^_^,那就另当别论。有序列的地方就有排序的需求。在各种排序算法都已经成熟的今天,我们完全可以针对特定元素类型的切片手写排序函数/方法,但多数情况...

weixin_40005330的博客 306
上一篇: Go语言的反射
下一篇: Go语言的tools包——编写自己的函数库
DXB2021
博客等级 码龄5年 307粉丝 289原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值