Go语言小示例---接口的使用

本文通过实例展示了Go语言接口的使用,包括构建可扩展的日志系统,利用接口进行数据排序,用空接口实现任意值存储的字典,以及设计有限状态机(FSM)。在日志系统部分,强调了接口方法格式一致性的重要性;在排序部分,介绍了sort.Interface的实现;字典部分展示了如何用map结合空接口存储不同类型数据;最后讨论了FSM的状态转换概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

日志可以用于查看和分析应用程序的运行状态。日志一般可以支持输出多种形式,如命令行、文件、网络。
本例将搭建一个支持多种写入器的日志系统。

package main

import (
	"errors"
	"fmt"
	"os"
)

/*
本例中定义一个日志写入器接口(LogWiter),必须遵循这个接口协议才能被日志器注册。
日志器有一个写入器的注册方法
*/
//-------日志对外的接口-------
//声明日志写入器接口
type LogWriter interface {
	Write(data interface{}) error
}

//日志器(类型)
type Logger struct {
	writerList []LogWriter //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{}
}

//----------文件写入器-------------
//文件写入器是众多日志写入器中的一种。

//声明文件写入器
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{}
}

//--------命令行写入器--------
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{}
}

//--------使用日志-----------
//创建日志
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 l
}

func main() {
	//准备日志器
	l := createLogger()

	//写一个日志
	l.Log("hello")
}

在log.log中还有命令行这块都输出了hello.

记一次小的语法错误:
在这里插入图片描述

这个错误可找死我了,最后发现,在命令行写入器那块实现接口中的方法Write写成了小写,一直不通过。
这就是犯了接口实现的条件一:接口的方法与实现接口的类型方法格式一致

使用接口进行数据的排序

Go语言中在排序时,需要使用者通过sort.Interface接口提供数据的一些特性和操作方法。这个接口需要实现者实现的方法进行排序操作:数量(Len)、比较(Less)、交换(Swap).
接口定义的代码如下:

type Interface interface{
	//获取元素数量
	Len() int 
	//小于比较
	Less(i,j int) bool 
	//交换元素
	Swap(i,j int)
}

1.对字符串切片进行排序

package main

import (
	"fmt"
	"sort"
)

type Interface interface {
	//获取元素数量
	Len() int
	//小于比较
	Less(i, j int) bool
	//交换元素
	Swap(i, j int)
}

//将[]string定义为MyStringList类型
/*要排序的字符串切片[]string是系统定制好的类型,
无法让这个类型去实现sort.Interface排序接口。因此需要将[]string定义为自定义的类型*/
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包会通过MyStringList实现的Len()、Less()、Swap()这3个方法进行数据获取和修改*/
	sort.Sort(names)

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

输出结果:
在这里插入图片描述
2.实现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)
	}
}

输出结果:
在这里插入图片描述
GO1.8中对切片进行排序,直接使用Sort.Slice进行排序.不用对less ,len这些方法进行实现。

	//使用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
	})

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

空接口可以保存任何类型的特性,方便用于容器的设计。
示例:使用map和interface{}实现字典的功能。

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,停止遍历
//Dictionary的Visit方法需要传入类型为func(k,v interface{}) bool的回调函数。
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 { //回调函数
		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

实现有限状态机(FSM)

1.状态的概念
状态机中的状态与状态间能够自由转换。每个状态可以设置它可以转移到的状态。一些状态机还允许在同一个状态间互相转换,这也需要根据实际情况进行配置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值