接口是双方约定的一种合作协议。接口实现者不需要关心接口会被怎样使用,调用者也不需要关心接口的实现细节。
接口是一种类型也是一种抽象结构,不会暴露所含数据的格式、类型及结构。
1、接口声明格式
type 接口类型名interface{
方法名1(参数列表1)返回值列表1
方法名2(参数列表2)返回值列表2
...
}
接口类型名:一般在字母最后加er
方法名:当方法名首字母是大写时,且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
参数列表、返回值列表:参数列表、返回值列表中的参数变量名可以被忽略。
2、开发中常见的接口及写法
io包中提供的Writer接口:
type Writer interface{
Write(p [byte])(n int,err error)
}
Stringer接口
type Stringer interface{
String() string
}
1、条件一:接口的方法与实现接口的类型方法格式一致
数据写入器的抽象
package main
import "fmt"
//定义一个数据写入器
type Data_Writer interface {
Write_Data(data interface{})error
}
//定义文件结构,用于实现Data_Writer
type file struct {
}
//实现Data_Writer接口的Write_Data()方法
func (d *file)Write_Data(data interface{})error {
//模拟写入数据
fmt.Println("Write_Data:",data)
return nil
}
func main() {
//实例化file
f:=new(file)
//声明一个Data_Write的接口
var writer Data_Writer
//将接口赋值f,也就是*file类型
writer=f
//使用Data_Write接口进行数据写入
writer.Write_Data("data")
}
本例中实现及调用关系图
2、条件二:接口中所有方法均被实现
当一个接口中有多个方法时,只有这些方法都被实现了,接口才能被正确编译使用。
1、一个类型可以实现多个接口
2、多个类型可以实现相同的接口
接口方法可以通过在类型中嵌入其他类型或者结构体来实现。
service开启服务的方法(Start())和输出日志的方法(Log())实现
package main
//一个服务需要满足能够开启和写日志的功能
type Service interface {
Start()//开启服务
Log(string)//日志输出
}
//日志器
type Logger struct {
}
//实现Service的Log()方法
func (g *Logger)Log(l string) {
}
//游戏服务
type Game_Service struct {
Logger//嵌入日志
}
//实现Service的Start()方法
func (s *Game_Service)Start() {
}
func main() {
var s Service=new(Game_Service)
s.Start()
s.Log("hello")
}
一个支持多种写入器的日志系统
日志写入器
package main
//声明日志写入接口
type Log_Writer interface {
Write(data interface{})error
}
//日志器
type Logger struct {
//这个日志器用到的日志写入器
writer_List []Log_Writer
}
//注册一个日志写入器
func (l *Logger) Register_Writer(writer Log_Writer) {
l.writer_List=append(l.writer_List,writer)
}
//将一个data类型的数据写入日志
func (l *Logger)Log(data interface{}) {
//遍历所有注册的写入器
for _,writer:=range l.writer_List{
//将日志输出到每个写入器中
writer.Write(data)
}
}
//创建日志器的实例
func New_Logger()*Logger {
return &Logger{}
}
(2)文件写入器
package main
import (
"errors"
"fmt"
"os"
)
//声明文件写入器
type file_Writer struct {
file *os.File
}
//设置文件写入器写入的文件名
func (f *file_Writer)Set_File(filename string)(err error) {
//如果文件已经打开,关闭前一个文件
if f.file!=nil{
f.file.Close()
}
//创建一个文件并保存文件句柄
f.file,err=os.Create(filename)
//如果创建的过程出现错误,则返回错误
return err
}
//实现Log_Writer的Write()方法
func (f *file_Writer)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 new_File_Writer()*file_Writer {
return &file_Writer{}
}
(3)命令行写入器
package main
import (
"fmt"
"os"
)
//命令行写入器
type console_Writer struct {
}
//实现Log_Writer的Write()方法
func (f *console_Writer)Write(data interface{})error {
//将数据序列化为字符串
str:=fmt.Sprintf("%v\n",data)
//将数据以字节数组写入命令行
_,err:=os.Stdout.Write([]byte(str))
return err
}
//创建命令行写入器实例
func new_Console_Writer()*console_Writer {
return &console_Writer{}
}
(4)使用日志
package main
import "fmt"
//创建日志器
func create_Logger()*Logger {
//创建日志器
l:=New_Logger()
//创建命令行写入器
cw:=new_Console_Writer()
//注册命令行写入器到日志器中
l.Register_Writer(cw)
//创建文件写入器
fw:=new_File_Writer()
//设置文件名
if err:=fw.Set_File("log.log");err!=nil{
fmt.Println(err)
}
//注册文件写入器到日志器中
l.Register_Writer(fw)
return l
}
func main() {
//准备日志器
l:=create_Logger()
//写日志
l.Log("hello")
}
用户自定义类型排序,需要实现sort.Interface接口的Len,Less和Swap三个方法。
字符串排序
package main
import (
"fmt"
"sort"
)
//将[]string 定义为My_String_List类型
type My_String_List []string
//实现sort.Interface接口的获取元素数量的方法
func (m My_String_List)Len()int {
return len(m)
}
//实现sort.Interface接口的比较元素的方法
func (m My_String_List)Less(i,j int)bool {
return m[i]
Go语言中的sort包中定义的一些常见类型的排序方法
3、对结构体数据进行排序
package main
import (
"fmt"
"sort"
)
//声明英雄分类
type Hero_Kind int
//定义Hero_Kind常量,类似于枚举
const(
None_Hero_Kind = iota
Tank
Assassin
Mage
)
//定义英雄名单的结构
type Hero struct {
Name string//英雄名
Kind Hero_Kind//英雄种类
}
//将英雄指针的切片定义为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
package main
import "io"
type Writer interface {
Write(p []byte)(n int,err error)
}
type Closer interface {
Close()error
}
//两个接口嵌入一个接口中
type WriteCloser interface {
Writer
Closer
}
//声明一个设备
type device struct {
}
//实现Writer的Write方法
func (d *device)Write(p []byte)(n int,err error) {
return 0,nil
}
//实现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)
}
1、类型断言格式
t:=i.(T)
i代表接口变量,T代表转换的目标类型,t代表转换后的变量。
更优的格式 t,ok:=i.(T)
如果接口未实现,则ok置为false,t置为T类型的0.
2、将接口转换为其他接口
实现某个接口的类型同时实现了另外一个接口,此时可以在两个接口间转换。
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,isWalk:=obj.(Walker)
fmt.Printf("name:%s is Flyer:%v isWalker: %v\n",name,isFlyer,isWalk)
//如果是飞行动物则调用飞行动物接口
if isFlyer{
f.Fly()
}
//如果是行走动物则调用行走动物接口
if isWalk{
w.Walk()
}
}
}
var any interface{}
any=1
fmt.Println(any)
any="hello"
fmt.Println(any)
空接口类型转换为其他类型需要使用类型断言实现。
3、空接口值比较
(1)不同类型的空接口间的比较结果不相同
(2)不能比较空接口中的动态值,如切片等
类型的可比较性:
package main
import "fmt"
//字典结构
type Dictionary struct {
data map[interface{}]interface{}
}
//根据键获取值
func (d *Dictionary)Get(key interface{})interface{} {
return d.data[key]
}
//设置键值
func (d *Dictionary)Set(key ,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 New_Dictionary()*Dictionary {
d:=&Dictionary{}
//初始化map
d.Clear()
return d
}
//使用字典
func main() {
//创建字典实例
dict:=New_Dictionary()
//添加游戏数据
dict.Set("My Favorite",60)
dict.Set("Terra Craft",36)
dict.Set("Don't Hungry",24)
//获取值并打印
favorite:=dict.Get("My Favorite")
fmt.Println("Favorite:",favorite)
//遍历所有的字典元素
dict.Visit(func(k, v interface{}) bool {
//将值转为int,并判断是否大于40
if v.(int)>40{
//输出很贵
fmt.Println(k,"is expensive")
return true
}
//默认输出很便宜
fmt.Println(k,"is cheap")
return true
})
}
1、类型断言的书写格式
switch 接口变量.(type){
case 类型1:
//变量是类型1时的处理
case 类型2:
//变量是类型2时的处理
...
default:
//变量不是所有case中列举类型时的处理
}
2、使用类型分支判断基本类型
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)
}
3、使用类型分支判断接口类型
package main
import "fmt"
//电子支付方式
type Alipay struct {
}
//为Alipay添加CauUseFaceID()方法,表示电子支付方式支持刷脸
func (a *Alipay)CanUseFaceID() {
}
//现金支付方式
type Cash struct {
}
//为Cash添加Stolen()方法,表示现金支付方式会出现偷窃情况
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 my be stolen\n",payMethod)
}
}
func main() {
//使用电子支付判断
print(new(Alipay))
//使用现金判断
print(new(Cash))
}
(1)状态机中的状态间可以自由转换。
(2)每个状态可以设置它可以转移到的状态。
状态接口
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()
}
状态基本信息
//协助用户实现一些默认的实现
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
}
状态管理器
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 not found")
//禁止在同状态间转移
var ErrForbidSameStateTransit = 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 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
}
自定义状态实现状态接口
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 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")
}
//封装转移状态和输出日志
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())
}
}