Golang语言系列-09-接口

接口

接口的定义和实现

package main

import "fmt"

/*
[接口]
接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节

在Go语言中接口(interface)是一种类型,一种抽象的类型,interface是一组method(方法)的集合

接口做的事情就像是定义一个协议(规则),只要一台机器有洗衣服和甩干的功能,我就称它为洗衣机。不关心属性(数据),只关心行为(方法)。
为了保护你的Go语言职业生涯,请牢记接口(interface)是一种类型

[为什么要使用接口?]
type Cat struct{}
func (c Cat) Say() string { return "喵喵喵" }

type Dog struct{}
func (d Dog) Say() string { return "汪汪汪" }

func main() {
	c := Cat{}
	fmt.Println("猫:", c.Say())
	d := Dog{}
	fmt.Println("狗:", d.Say())
}
上面的代码中定义了猫和狗,然后它们都会叫,你会发现 main 函数中明显有重复的代码,
如果我们后续再加上猪、青蛙等动物的话,我们的代码还会一直重复下去。
那我们能不能把它们当成“能叫的动物”来处理呢?
Go语言中为了解决类似上面的问题,就设计了接口这个概念。
接口区别于我们之前所有的具体类型,接口是一种抽象的类型。
当你看到一个接口类型的值时,你不知道它是什么,唯一知道的是通过它的方法能做什么。

Go语言提倡面向接口编程
每个接口由数个方法组成,接口的定义格式如下
type 接口类型名 interface{
    方法名1( 参数列表1 ) 返回值列表1
    方法名2( 参数列表2 ) 返回值列表2
    …
}
其中:
	接口名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,
           如有写操作的接口叫Writer,有字符串功能的接口叫Stringer等。
           接口名最好要能突出该接口的类型含义。
	方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
	参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略


[实现接口的条件]
一个对象只要 全部实现 了接口中的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表
如果接口中有没被实现的方法,那么就会报错.
结构体实现接口中的方法,要么全部实现,要么就一个别实现,否则报错

[接口类型变量]
那实现了接口有什么用呢?
接口类型变量能够存储所有实现了该接口的实例。 例如上面的示例中,Sayer类型的变量能够存储dog和cat类型的变量。
*/

// 示例
// 由于猫、狗、人都能叫
// 所以定义一个能叫的接口
type speaker interface {
	speak() //只要实现了speak方法的变量都是speaker类型, 方法签名
}

//定义结构体
type cat struct{}

type dog struct{}

type person struct{}

// 定义相对应的方法,实现接口
func (c cat) speak() {
	fmt.Println("喵喵喵~")
}

func (d dog) speak() {
	fmt.Println("旺旺旺~")
}

func (p person) speak() {
	fmt.Println("啊啊啊~")
}

// 定义一个函数
func da(x speaker) {
	// 接收一个参数,传进来什么,我就打什么
	x.speak() // 挨打了就要叫
}

func main() {
	var c1 cat
	var d1 dog
	var p1 person

	da(c1)
	da(d1)
	da(p1)

	var ss speaker // 定义一个接口类型:speaker 的变量:ss
	ss = c1
	fmt.Printf("%T\n", ss) //main.cat
	ss = d1
	fmt.Printf("%T\n", ss) //main.dog
	ss = p1
	fmt.Printf("%T\n", ss) //main.person
}
package main

import "fmt"

// 定义支付接口
type payer interface {
	pay()
}

// 定义支付函数
func pay(p payer) {
	p.pay()
}

// 定义结构体
type weixin struct {
	name string
}

// 定义结构体方法 实现接口中的方法
func (w weixin) pay() {
	fmt.Printf("尊敬的[%s],欢迎使用微信支付~\n", w.name)
}

// 定义结构体
type alipay struct {
	name string
}

// 定义结构体方法 实现接口中的方法
func (a alipay) pay() {
	fmt.Printf("欢迎[%s]使用阿里支付~\n", a.name)
}

func main() {
	var w weixin
	var a alipay
	w.name = "Alnk"
	a.name = "tom"
	pay(w)
	pay(a)

	////实现接口以后就不用每次都这么写重复的代码了
	//p1 := weixin{"Alnk"}
	//p1.pay()
	//p2 := alipay{"tom"}
	//p2.pay()
}
package main

import "fmt"

// 接口示例2
// 不管什么牌子的车都能跑

// 定义一个car接口类型
// 不管是什么结构体,只要有run方法都能是carer类型
type carer interface {
	run() //只要实现了run方法的变量都是carer类型, 方法签名
}

//drive 定义一个函数
func drive(c carer) {
	c.run()
}

// falali 定义一个结构体
type falali struct {
	name string
}

// falali 的run方法
func (f falali) run() {
	fmt.Printf("%s的速度70\n", f.name)
}

// baoshijie 定义一个结构体
type baoshijie struct {
	name string
}

// baoshijie 的run方法
func (b baoshijie) run() {
	fmt.Printf("%s的速度100\n", b.name)
}

func main() {
	f := falali{"法拉利"}
	//f.run()
	drive(f)

	b := baoshijie{"保时捷"}
	drive(b)
}
package main

import "fmt"

// 接口的实现

// 定义一个动物接口
type animal interface {
	move()
	eat(string)
}

// 定义一个猫的结构体
type cat struct {
	name string
	feet int8
}

// 猫结构体方法
func (c cat) move() {
	fmt.Println("走猫步...")
}

// 猫结构体方法
func (c cat) eat(food string) {
	fmt.Printf("猫吃%s...\n", food)
}

// 定义一个鸡的结构体
type chicken struct {
	feet int8
}

// 鸡结构体方法
func (c chicken) move() {
	fmt.Println("鸡动!")
}

// 鸡结构体方法
func (c chicken) eat(food string) {
	fmt.Printf("吃%s...\n", food)
}

func main() {
	var a1 animal          //定义一个接口类型的变量
	fmt.Printf("%T\n", a1) //<nil>

	// 定义一个cat类型的变量bc
	bc := cat{
		name: "淘气",
		feet: 4,
	}
	a1 = bc
	a1.eat("小黄鱼")          //猫吃小黄鱼...
	fmt.Printf("%T\n", a1) //main.cat
	fmt.Println(a1)        //{淘气 4}
	fmt.Println(bc)        //{淘气 4}

	kfc := chicken{feet: 2}
	a1 = kfc
	fmt.Printf("%T\n", a1) //main.chicken
	a1.eat("饲料")           //吃饲料...
}

值接收者和指针接收者的区别

package main

import "fmt"

// 使用值接收者和指针接收者的区别?
// 使用值接收者实现接口,结构体类型和结构体指针类型的变量都能存
// 使用指针接收者实现接口,只能存结构体指针类型的变量

// Mover接口和一个dog结构体
type Mover interface {
	move()
}

type dog struct{}

//// 值接收者实现接口
//func (d dog) move() {
//	fmt.Println("狗会动")
//}
//// 实现接口的是dog类型
//func main() {
//	var x Mover
//	var wangcai = dog{} // 旺财是dog类型
//	x = wangcai         // x可以接收dog类型
//	x.move()
//	var fugui = &dog{} // 富贵是*dog类型
//	x = fugui          // x可以接收*dog类型
//	x.move()
//}
//
///*
//从上面的代码中我们可以发现,使用值接收者实现接口之后,
//不管是 dog结构体 还是 结构体指针*dog类型 的变量都可以赋值给该接口变量。
//因为Go语言中有对指针类型变量求值的语法糖,
//dog指针fugui内部会自动求值*fugui
//*/

// 指针接收者实现接口
func (d *dog) move() {
	fmt.Println("狗会动")
}

func main() {
	var x Mover
	//var wangcai = dog{} // 旺财是dog类型
	//x = wangcai         // x不可以接收dog类型
	var fugui = &dog{} // 富贵是*dog类型
	x = fugui          // x可以接收*dog类型
	x.move()
}

// 此时实现Mover接口的是*dog类型,所以不能给x传入dog类型的wangcai,此时x只能存储*dog类型的值fugui
package main

import "fmt"

// 注意:这是一道你需要回答“能”或者“不能”的题!
// 首先请观察下面的这段代码,然后请回答这段代码能不能通过编译?

type People interface {
	Speak(string) string
}

type Student struct{}

func (stu *Student) Speak(think string) (talk string) {
	if think == "sb" {
		talk = "你是个大帅比"
	} else {
		talk = "您好"
	}
	return
}

func main() {
	var peo People
	peo = Student{}
	//peo = &Student{} //注意这里要返回指针类型才行,因为方法中接收的是指针类型

	think := "bitch"
	fmt.Println(peo.Speak(think))
}

接口和类型的关系

package main

import "fmt"

// 一个类型可以实现多个接口
// 同一个结构体可以实现多个接口
// 接口还可以嵌套

// 接口
type animal interface {
	mover
	eater
}

// 接口
type mover interface {
	move()
}

// 接口
type eater interface {
	eat(string)
}

// cat结构体
type cat struct {
	name string
	feet int8
}

// cat实现了mover接口
func (c *cat) move() {
	fmt.Println("走猫步...")
}

// cat实现了eater接口
func (c *cat) eat(food string) {
	fmt.Printf("猫吃%s...\n", food)
}

func main() {
	c1 := cat{
		name: "tom",
		feet: 4,
	}

	var a1 animal

	a1 = &c1
	a1.move()     // 走猫步...
	a1.eat("小黄鱼") // 猫吃小黄鱼...

	var m1 mover
	m1 = &c1
	m1.move() // 走猫步...

	var e1 eater
	e1 = &c1
	e1.eat("鱼仔") // 猫吃鱼仔...
}

空接口

package main

import "fmt"

// 空接口
// 空接口是指没有定义任何方法的接口
// 因此任何类型都实现了空接口
// 空接口类型的变量可以存储任意类型的变量

// interface:关键字
// interface{}:空接口类型

// 空接口的应用:
// 1.空接口作为函数的参数
// 2.空接口作为map的值

// 空接口作为函数参数
func show(a interface{}) {
	fmt.Printf("type:%T --- value:%v\n", a, a)
}

func main() {
	// 空接口作为map的值
	var m1 map[string]interface{}
	m1 = make(map[string]interface{}, 16)
	m1["name"] = "周林"
	m1["age"] = 9000
	m1["merried"] = true
	m1["hobby"] = [...]string{"唱", "跳", "rap"}
	fmt.Printf("%#v\n", m1) //map[string]interface {}{"age":9000, "hobby":[3]string{"唱", "跳", "rap"}, "merried":true, "name":"周林"}

	// 空接口作为函数参数
	show(false) //type:bool --- value:false
	show(nil)   //type:<nil> --- value:<nil>
	show(m1)    //type:map[string]interface {} --- value:map[age:9000 hobby:[唱 跳 rap] merried:true name:周林]

	// 空接口作为map的键和值
	var m2 map[interface{}]interface{}
	m2 = make(map[interface{}]interface{}, 5)
	m2[1] = "a"
	m2["b"] = true
	m2[true] = 1
	fmt.Printf("%#v\n", m2) //map[interface {}]interface {}{true:1, 1:"a", "b":true}
	show(m2)                //type:map[interface {}]interface {} --- value:map[true:1 1:a b:true]
}

类型断言

package main

import "fmt"

/*
[类型断言]
空接口可以存储任意类型的值,那我们如何获取其存储的具体数据呢

一个接口的值(简称接口值)是由一个 具体类型 和 具体类型的值 两部分组成的
这两部分分别称为 接口的动态类型 和 接口的动态值

想要判断空接口中的值这个时候就可以使用类型断言,其语法格式:
x.(T)
其中:
	x:表示类型为interface{}的变量
	T:表示断言x可能是的类型
该语法返回两个参数,第一个参数是x转化为T类型后的变量,第二个值是一个布尔值,若为true则表示断言成功,为false则表示断言失败

因为空接口可以存储任意类型值的特点,所以空接口在Go语言中的使用十分广泛

重点:关于接口需要注意的是,只有当 [有两个或两个以上的具体类型] 必须以相同的方式进行处理时才需要定义接口。
不要为了接口而写接口,那样只会增加不必要的抽象,导致不必要的运行时损耗
*/

// 类型断言1
func assign(a interface{}) {
	fmt.Printf("%T\n", a)
	str, ok := a.(string)
	if !ok {
		fmt.Println("猜错了")
	} else {
		fmt.Println("传进来的是一个字符串: ", str)
	}
}

// 类型断言2
// x.(type)
// x表示类型为interface{}的变量
// type获取x变量的动态类型
func assign2(a interface{}) {
	fmt.Printf("%T\n", a)
	switch t := a.(type) {
	case string:
		fmt.Println("这是一个字符串:", t)
	case int:
		fmt.Println("这是一个int:", t)
	case int64:
		fmt.Println("这是一个int64:", t)
	case bool:
		fmt.Println("这是一个bool:", t)
	case []int:
		fmt.Println("这是一个slice:", t)
	case map[string]int:
		fmt.Println("是一个map[string]int:", t)
	case func():
		fmt.Println("这是一个函数类型:", t)
	}
}

func main() {
	//assign("100")
	assign2(true)
	assign2("啊哈哈")
	assign2(int64(200))
	assign2([]int{1, 2, 3})
	assign2(map[string]int{"a": 1})
}

接口练习

session中间件开发练习

目录结构

02session中间件开发
├── main.go //测试session中间件
└── session
    ├── docs
    │   ├── README
    │   └── 架构图.png
    ├── memory.go
    ├── memory_session_mgr.go
    ├── session.go
    └── session_mgr.go

README

设计一个通用的Session服务,支持内存存储和redis存储

session模块设计
    本质上k-v系统,通过key进行增删改查
    session可以存储在内存或者redis(2个版本)

    每一个用户 ---> 一个session ---> 多个key-value
    session ---> sessionMgr (多个session对应一个sessionMrg  多对一)


接口设计
Session接口设计:
    Set()
    Get()
    Del()
    Save():session存储,redis的实现延迟加载

SessionMgr接口设计:
    Init():初始化,加载redis地址
    CreateSeesion():创建一个新的session
    GetSession():通过sessionId获取对应的session对象

MemorySeesion设计:
    定义MemorySeesion对象(字段:sessionId、存kv的map,读写锁)
    构造函数,为了获取对象
    Set()
    Get()
    Del()
    Save()

MemorySeesionMgr设计:
    定义MemorySeesionMgr对象(字段:存放所有session的map,读写锁)
    构造函数
    Init()
    CreateSeesion()
    GetSession()


RedisSession设计:
    定义RedisSeesion对象(字段:sessionId,存kv的map,读写锁,redis连接池,记录内存中map是否被修改的标记)
    构造函数
    Set():将session存到内存中的map
    Get():取数据,实现延迟加载
    Del()
    Save():将session存到redis


RedisSessionMgr设计:
    定义RedisSessionMgr对象(字段:redis地址、redis密码、连接池、读写锁,大map)
    构造函数
    Init()
    CreateSeesion()
    GetSession()

调用示例
如果要在多个函数中使用,设置一个全局的 session.NewMemorySessionMgr() 变量
// 设置session
memSess := session.NewMemorySessionMgr()
s1, id1, err := memSess.CreateSession() // 创建一条session
if err != nil {
    panic(err)
}
s1.Set("name", "alnk")   // 在一条session中设置值
s1.Set("isLogin", false) // 在一条session中设置值
s1.Set("isVip", false)   // 在一条session中设置值

// 获取一条session
ms1, _ := memSess.GetSession(id1) // 获取单个session
// 获取单个session里面的key-value
name1, err := ms1.Get("name")
login1, err := ms1.Get("isLogin")
vip1, err := ms1.Get("isVip")
fmt.Println(name1, login1, vip1)

### redisSession功能未完成

架构图.png

session.go

package session

// 只定义session接口规范
// 其他的模块只要实现这些规范即可

type Session interface {
	Set(key string, value interface{}) error
	Get(key string) (interface{}, error)
	Del(key string) error
	Save() error
}

session_mgr.go

package session

// 定义接口规范

// SessionMgr 定义管理者,管理所有的session
type SessionMgr interface {
	// 初始化
	Init(addr string, options ...string) (err error)
	CreateSession() (session Session, sessionId string, err error)
	GetSession(sessionId string) (session Session, err error)
}

memory.go

package session

import (
	"errors"
	"sync"
)

// 实现Session接口

type MemorySession struct {
	sessionId string
	// 存key-value
	data   map[string]interface{}
	rwlock sync.RWMutex // 读写锁,不然如果数据量太大,map读写会出问题
}

// 构造函数
func NewMemorySession(id string) *MemorySession {
	s := &MemorySession{
		sessionId: id,
		data:      make(map[string]interface{}, 16),
	}
	return s
}

func (m *MemorySession) Set(key string, value interface{}) (err error) {
	// 加锁
	m.rwlock.Lock()
	defer m.rwlock.Unlock()
	// 设置值
	m.data[key] = value
	return
}

func (m *MemorySession) Get(key string) (value interface{}, err error) {
	m.rwlock.Lock()
	defer m.rwlock.Unlock()
	value, ok := m.data[key]
	if !ok {
		err = errors.New("key not exists in session")
		return
	}
	return
}

func (m *MemorySession) Del(key string) (err error) {
	m.rwlock.Lock()
	defer m.rwlock.Unlock()
	delete(m.data, key)
	return
}

func (m *MemorySession) Save() (err error) {
	return
}

memory_session_mgr.go

package session

import (
	"errors"
	"github.com/satori/go.uuid"
	"sync"
)

// 实现 SessionMgr 接口

// MemorySessionMgr设计:
// 定义MemorySessionMgr对象(字段:存放所有session的map,读写锁)
// 构造函数
// Init()
// CreateSession()
// GetSession()

// 定义对象
type MemorySessionMgr struct {
	sessionMap map[string]Session
	rwlock     sync.RWMutex
}

// 构造函数
func NewMemorySessionMgr() SessionMgr {
	sr := &MemorySessionMgr{
		sessionMap: make(map[string]Session, 1024),
	}
	return sr
}

func (s *MemorySessionMgr) Init(addr string, option ...string) (err error) {
	return
}

// 创建一条session
func (s *MemorySessionMgr) CreateSession() (session Session, sessionId string, err error) {
	s.rwlock.Lock()
	defer s.rwlock.Unlock()
	// go get github.com/satori/go.uuid
	// 用uuid作为 sessionId
	id := uuid.NewV4()
	// 转 string
	sessionId = id.String()
	// 创建单个session
	session = NewMemorySession(sessionId)
	// 加入到大map
	s.sessionMap[sessionId] = session
	return
}

// 获取一条session
func (s *MemorySessionMgr) GetSession(sessionId string) (session Session, err error) {
	s.rwlock.Lock()
	defer s.rwlock.Unlock()
	session, ok := s.sessionMap[sessionId]
	if !ok {
		err = errors.New("session not exists")
		return
	}
	return
}

main.go

package main

import (
	"02session中间件开发/session"
	"fmt"
)

var memSess = session.NewMemorySessionMgr() // 设置为全局变量,好让所以的函数都能调用

func main() {
	s, id, err := memSess.CreateSession() // 创建一条session
	if err != nil {
		panic(err)
	}
	fmt.Println("id:", id)

	// 在session中设置值
	name := "tom"
	isLogin := true
	isVip := true
	s.Set("name", name)       // 在一条session中设置值
	s.Set("isLogin", isLogin) // 在一条session中设置值
	s.Set("isVip", isVip)     // 在一条session中设置值

	// 获取一条session
	rs, err := memSess.GetSession(id)
	fmt.Printf("rs:%#v\n", rs)
	fmt.Println(rs)
}

posted @ 2021-03-11 10:35  李成果  阅读(188)  评论(0编辑  收藏  举报