8、接口与面向接口编程

一、接口的基本概念

  • 接口的定义:接口是一组行为规范的集合
type Transporter interface {
    
     //定义接口,通常接口名以er结尾
	//接口里面只定义方法,不定义变量
	move(src string, dest string) (int, error) //方法名 (参数列表) 返回值列表
	whistle(int) int                           //参数列表和返回值列表里的变量名可以省略
}
  • 接口的实现
    • 只要结构体拥有接口里声明的所有方法,就称该结构体“实现了接口”
    • 一个struct可以同时实现多个接口
type Car struct {
    
     //定义结构体时无需要显式声明它要实现什么接口
	price int
}

func (car Car) move(src string, dest string) (int, error) {
    
    
	return car.price, nil
}
func (car Car) whistle(n int) int {
    
    
	return n
}
  • 接口的本质:接口值有两部分组成
    • 一个指向该接口的具体类型的指针
    • 另外一个指向该具体类型真实数据的指针
      在这里插入图片描述
type Transporter interface {
    
     //定义接口,通常接口名以er结尾
	//接口里面只定义方法,不定义变量
	move(src string, dest string) (int, error) //方法名 (参数列表) 返回值列表
	whistle(int) int                           //参数列表和返回值列表里的变量名可以省略
}

type Car struct {
    
     //定义结构体时无需要显式声明它要实现什么接口
	name  string
	price int
}

func (car Car) move(src string, dest string) (int, error) {
    
    
	return car.price, nil
}
func (car Car) whistle(n int) int {
    
    
	return n
}

func main() {
    
    
	car := Car{
    
    "宝马", 100}
	var transpoter Transporter
	transpoter = car
	transpoter.whistle(3)
}
  • 接口的使用:实现了接口后,在函数中传递接口参数作为形参,可以使用具体的接口实现作为实参
type Transporter interface {
    
     //定义接口,通常接口名以er结尾
	//接口里面只定义方法,不定义变量
	move(src string, dest string) (int, error) //方法名 (参数列表) 返回值列表
	whistle(int) int                           //参数列表和返回值列表里的变量名可以省略
}

type Car struct {
    
     //定义结构体时无需要显式声明它要实现什么接口
	name  string
	price int
}

func (car Car) move(src string, dest string) (int, error) {
    
    
	return car.price, nil
}
func (car Car) whistle(n int) int {
    
    
	return n
}

type Ship struct {
    
     //定义结构体时无需要显式声明它要实现什么接口
	name  string
	price int
}

func (ship Ship) move(src string, dest string) (int, error) {
    
    
	return ship.price, nil
}
func (ship Ship) whistle(n int) int {
    
    
	return n
}

// 实现了接口后,在函数中传递接口参数作为形参,可以使用具体的接口实现作为实参
func transport(src, dest string, transpoter Transporter) error {
    
    
	_, err := transpoter.move(src, dest)
	return err
}

func main() {
    
    
	var car Car   //Car实现了Transporter接口
	var ship Ship //Ship实现了Transporter接口
	transport("北京", "天津", car)
	transport("北京", "天津", ship)
}
  • 接口的赋值
    • 值实现的方法,指针也同样实现了
    • 指针实现的方法,只能使用指针进行接口的赋值
type Car struct {
    
     //定义结构体时无需要显式声明它要实现什么接口
	name  string
	price int
}

func (car Car) move(src string, dest string) (int, error) {
    
    
	return car.price, nil
}
func (car Car) whistle(n int) int {
    
    
	return n
}

type Ship struct {
    
     //定义结构体时无需要显式声明它要实现什么接口
	name  string
	price int
}

func (ship *Ship) move(src string, dest string) (int, error) {
    
    
	return ship.price, nil
}
func (ship *Ship) whistle(n int) int {
    
    
	return n
}

func transport(src, dest string, transpoter Transporter) error {
    
    
	_, err := transpoter.move(src, dest)
	return err
}

func main() {
    
    
	var car Car   //Car实现了Transporter接口
	var ship Ship //Ship实现了Transporter接口
	transport("北京", "天津", car)
	transport("北京", "天津", &car)
	// transport("北京", "天津", ship) //错误,指针实现的方法不能使用值赋值
	transport("北京", "天津", &ship) //指针实现的方法只能使用指针赋值
}
  • 接口嵌入
    • Transporter定义了一组行为规范
    • Steamer定义了一组更大的行为规范
    • 实现Steamer接口的时候,需要同时实现Steamer的displacement行为以及Transporter的whistle行为
type Transporter interface {
    
    
	whistle(int) int
}

type Steamer interface {
    
    
	Transporter //接口嵌入,相当于Transporter接口定义的行为集合是Steamer的子集
	displacement() int
}

二、类型断言

  • 类型断言
func main() {
    
    
	var i interface{
    
    }
	if v, ok := i.(int); ok {
    
     //若断言成功,则ok为true,v是具体的类型
		fmt.Printf("i是int类型,其值为%d\n", v)
	} else {
    
    
		fmt.Println("i不是int类型")
	}
	//i不是int类型
}
  • 当要判断的类型比较多时,更好的方法是使用switch i.(type)
func assert(i interface{
    
    }) {
    
    
	switch v := i.(type) {
    
     //隐式地在每个case中声明了一个变量v
	case int: //v已被转为int类型
		fmt.Printf("%d\n", v)
	case float64:
		fmt.Printf("%f\n", v)
	case byte, uint16, string: //如果case后面跟多种type,则v还是interface{}类型
		fmt.Printf("%T %v\n", v, v)
	}
}

func assert2(i interface{
    
    }) {
    
    
	switch i.(type) {
    
    
	case int:
		v := i.(int)
		fmt.Printf("%d\n", v)
	case float64:
		v := i.(float64)
		fmt.Printf("%f\n", v)
	case byte, uint16, string:
		fmt.Printf("%T %v\n", i, i)
	}
}

func main() {
    
    
	var i interface{
    
    }
	var a int
	var b float64
	var c byte

	i = a
	assert(i)  //0
	assert2(i) //0

	i = b
	assert(i)  //0.000000
	assert2(i) //0.000000

	i = c
	assert(i)  //uint8 0
	assert2(i) //uint8 0
}

三、面向接口编程

  • 推荐流程
    在这里插入图片描述
  • 为每一个步骤定义一个接口
    • Recommender中包含了推荐接口的切片、排序接口、过滤接口的切片
// 商品
type Product struct {
    
    
	Id            int
	Name          string
	Size          int     //产品尺寸
	Sale          int     //销量
	ShipAddress   string  //发货地址
	Price         float64 //单价
	PositiveRatio float64 //好评率
	RatioCount    int     //评论量
}

type Recaller interface {
    
    
	Recall(n int) []*Product //生成一批召回候选集
}
type Sorter interface {
    
    
	Sort([]*Product) []*Product //传入一批商品,返回排序之后的商品
}
type Filter interface {
    
    
	Filter([]*Product) []*Product //传入一批商品,返回过滤之后的商品
}
type Recommender struct {
    
    //推荐组合
	Recallers []Recaller
	Sorter    Sorter
	Filters   []Filter
}

func (rec *Recommender) Rec() []*Product {
    
    
	RecallMap := make(map[int]*Product, 100)
	//顺序执行多路召回
	for _, recaller := range rec.Recallers {
    
    
		products := recaller.Recall(10) //统一设置每路最多召回10个商品
		for _, product := range products {
    
    
			RecallMap[product.Id] = product //把多路召回的结果放到map里,按Id进行排重
		}
	}
	//把map转成slice
	RecallSlice := make([]*Product, 0, len(RecallMap))
	for _, product := range RecallMap {
    
    
		RecallSlice = append(RecallSlice, product)
	}
	SortedResult := rec.Sorter.Sort(RecallSlice) //对召回的结果进行排序
	//顺序执行多种过滤规则
	FilteredResult := SortedResult
	for _, filter := range rec.Filters {
    
    
		FilteredResult = filter.Filter(FilteredResult)
	}
	return FilteredResult
}
  • 热门 -> 召回接口实现
type HotRecall struct {
    
    
	Tag string
}

func (hotrecall HotRecall) Name() string {
    
    
	return hotrecall.Tag
}
func (hotrecall HotRecall) Recall(n int) []*Product {
    
    
	//按销量降序排序
	sort.Slice(allProducts, func(i, j int) bool {
    
    
		return allProducts[i].Sale > allProducts[j].Sale
	})
	//返回前n个
	rect := make([]*Product, 0, n)
	for i, ele := range allProducts {
    
    
		if i >= n {
    
    
			break
		}
		rect = append(rect, ele)
	}
	return rect
}
  • size -> 召回接口实现
type SizeRecall struct {
    
    
	Tag string
}

func (sizerecall SizeRecall) Name() string {
    
    
	return sizerecall.Tag
}
func (sizerecall SizeRecall) Recall(n int) []*Product {
    
    
	rect := make([]*Product, 0, n)
	for _, ele := range allProducts {
    
    
		if ele.Size < 200 {
    
     //只召回size小于200的商品
			rect = append(rect, ele)
			if len(rect) >= n {
    
    
				break
			}
		}
	}
	return rect
}
  • size -> 排序接口实现
type SizeSorter struct {
    
    
	Tag string
}

func (sizesorter SizeSorter) Name() string {
    
    
	return sizesorter.Tag
}

func (sizesorter SizeSorter) Sort(products []*Product) []*Product {
    
    
	sort.Slice(products, func(i, j int) bool {
    
    
		//按尺寸升序排列
		return products[i].Size > products[j].Size
	})
	return products
}
  • 评价 -> 排序接口实现
type RatioSorter struct {
    
    
	Tag string
}

func (ratiosorter RatioSorter) Name() string {
    
    
	return ratiosorter.Tag
}

func (ratiosorter RatioSorter) Sort(products []*Product) []*Product {
    
    
	sort.Slice(products, func(i, j int) bool {
    
    
		//按好评率降序排列
		return products[i].PositiveRatio > products[j].PositiveRatio
	})
	return products
}
  • main测试函数
var (
	allProducts = []*Product{
    
    
		{
    
    Id: 1, Name: "小勺", Size: 35, Sale: 3582, ShipAddress: "郑州", Price: 6.9, PositiveRatio: 0.76, RatioCount: 364},
		{
    
    Id: 2, Name: "袜子", Size: 23, Sale: 43654, ShipAddress: "郑州", Price: 6546, PositiveRatio: 0.86, RatioCount: 7},
		{
    
    Id: 3, Name: "裤子", Size: 354, Sale: 54, ShipAddress: "郑州", Price: 547, PositiveRatio: 0.96, RatioCount: 5436},
		{
    
    Id: 4, Name: "裙子", Size: 675, Sale: 756, ShipAddress: "郑州", Price: 5423, PositiveRatio: 0.86, RatioCount: 6452},
		{
    
    Id: 5, Name: "袖子", Size: 23, Sale: 423, ShipAddress: "郑州", Price: 64, PositiveRatio: 0.88, RatioCount: 235},
		{
    
    Id: 6, Name: "iPad", Size: 65, Sale: 3, ShipAddress: "郑州", Price: 87, PositiveRatio: 0.96, RatioCount: 254},
		{
    
    Id: 7, Name: "iPhone", Size: 146, Sale: 254, ShipAddress: "北京", Price: 143, PositiveRatio: 0.90, RatioCount: 254},
		{
    
    Id: 8, Name: "电脑", Size: 4, Sale: 543, ShipAddress: "北京", Price: 2354, PositiveRatio: 0.91, RatioCount: 5435},
		{
    
    Id: 9, Name: "机床", Size: 65, Sale: 8, ShipAddress: "北京", Price: 76, PositiveRatio: 0.44, RatioCount: 4213},
		{
    
    Id: 10, Name: "袜子", Size: 76, Sale: 143, ShipAddress: "北京", Price: 14, PositiveRatio: 0.68, RatioCount: 543},
		{
    
    Id: 11, Name: "裤子", Size: 67, Sale: 6354, ShipAddress: "北京", Price: 65, PositiveRatio: 0.89, RatioCount: 5436},
		{
    
    Id: 12, Name: "裙子", Size: 325, Sale: 4, ShipAddress: "北京", Price: 124, PositiveRatio: 0.85, RatioCount: 534},
	}
)

func main() {
    
    
	rec := Recommender{
    
    
		//每种具体的实现可能是由不同的开发者完成。每种实现单独放一个文件,大家的代码互不干扰
		Recallers: []Recaller{
    
    HotRecall{
    
    Tag: "hot"}, SizeRecall{
    
    Tag: "size"}},
		Sorter:    RatioSorter{
    
    Tag: "ratio"},
		Filters:   []Filter{
    
    AddressFilter{
    
    Tag: "address", City: "郑州"}, RatioFilter{
    
    Tag: "ratio"}},
	}
	// rec.Sorter = sort.SizeSorter{Tag: "size"}
	result := rec.Rec()
	for i, product := range result {
    
    
		fmt.Printf("第%d名:%d %s\n", i, product.Id, product.Name)
	}

	// 第0名:3 裤子
	// 第1名:6 iPad
	// 第2名:5 袖子
	// 第3名:4 裙子
}

猜你喜欢

转载自blog.csdn.net/qq23001186/article/details/129128478
今日推荐