8、接口与面向接口编程

news2025/2/27 14:10:27

目录

  • 一、接口的基本概念
  • 二、类型断言
  • 三、面向接口编程

一、接口的基本概念

  • 接口的定义:接口是一组行为规范的集合
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的whistle行为以及Transporter的displacement行为
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 裙子
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/361334.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

10.现代循环神经网络

10.现代循环神经网络 目录 门控循环单元&#xff08;GRU&#xff09;门控隐状态 重置门和更新门候选隐状态 隐状态从零开始实现 初始化模型参数定义模型训练与预测 简洁实现总结 长短期记忆网络&#xff08;LSTM&#xff09; 门控记忆元 输入门、忘记门和输出门候选记忆元记忆…

基于xxx开发板的bluez的移植

基于xxx开发板的bluez的移植1.硬件电路2.软件准备2.1.源码配置2.2 编译源码3.请等待《题外话》&#xff1a;刚开始第一次接触bluez&#xff0c;完全没用过&#xff0c;也没搞过&#xff0c;开局一脸懵逼。刚好项目需要用到&#xff0c;只能硬着头皮上&#xff0c;淦淦淦&#x…

C语言之通讯录的实现

通讯录实现所需头文件和源文件 Contact.h的功能 声明函数和创建结构体变量 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> #include <assert.h> #define MAX 1000 #define MAX_NAME 20 #define MAX…

npm 上传自己的包

mkdir demo 创建一个新的文件夹 npm init 初始化项目 生成一个package.json文件 name version description等等touch index.js 创建一个node 可执行脚本新的js 文件 #!/usr/bin/env node // 必须在文件头加如上内容指定运行环境为node console.log(hello cli)在package.json 中…

Zookeeper框架

Zookeeper框架概述 1.Zookeeper介绍 Zookeeper&#xff08;以下简称ZK&#xff09;是用来管理和协调其他框架的&#xff0c;很多框架需要依赖ZK&#xff08;例如Hadoop-HA&#xff0c;Kafka&#xff0c;HBase等&#xff09;ZK本身也是一个集群ZK本身也可以存数据(一般保存配置…

手撸一个Switch开关组件

一、前言 手撸系列又来了&#xff0c;这次咱们来撸一个Switch开关组件&#xff0c;废话不多说&#xff0c;咱们立刻发车。 二、使用效果 三、实现分析 首先我们先不想它的这个交互效果&#xff0c;我们就实现“不合格”时的一个静态页面&#xff0c;静态页面大致如下&#x…

GeoServer如何发布PostgreSQL里的数据?

GIS服务端避免不了将数据存储在pg库里。本篇我们来说如何将其发布在geoserver上。 我们讲的全面一点,尽量从0开始,让小白都能看得懂。 首先假设你有一份shape数据,你可以同过postgis插件导入到pg数据库中。 这里要注意:导入的shape文件和路径都不能含有中文! 导入之前…

嵌入式linux驱动学习-用cdev代替register_chrdev()

​上回说到字符设备驱动程序的注册与销毁register_chrdev()和unregister_chrdev()这是有缺陷的。 嵌入式lnux驱动学习-2.一个驱动程序的流程 现在用另外一个更好的方法代替&#xff0c;我们先来看看register_chrdev()实际上是调用了 __register_chrdev(major, 0, 256, name,…

【Mysql系列】Mysql之ACID实现原理

ACID 原子性 事务不可分割&#xff0c;要么全部执行&#xff0c;要么都不执行。原理是使用undo log。undo log&#xff0c;当事务对数据库进行修改的时候&#xff0c;会生成对应的undo log。 持久性 事务提交后&#xff0c;对于数据库的改变是永久性的。实现原理通过redo l…

leaflet 清除底图以外的所有图层(两种方法)

第084个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet项目中清除除了底图以外的其他图层,这里有两种方法,详情请参考源代码。 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共140行)相关API专栏目标…

C++调用Python脚本进行18次循环操作后,脚本不执行

C调用Python脚本进行18次循环操作后&#xff0c;脚本不执行 现象&#xff1a; 发送端接收端 从第二张图中可以看出&#xff0c;python脚本卡在’[parkin_debug] 6’与’[parkin_debug] 7’之间 该测试经过多次反复测试&#xff0c;均在第18次循环执行时&#xff0c;出现上述问…

java TCP/UDP、Socket、URL网络编程详解

文章目录网络通信协议通信双方地址端口号IP地址InetAddress类Socket 网路编程Socket类的常用构造器Socket类的常用方法UDP协议什么是UDP协议UDP网络编程DatagramSocket 构造方法DatagramSocket 常用方法DatagramPacket常用方法实现步骤单向数据发收的UDP程序双向数据发收的UDP程…

社团结构的划分及实现过程

社团结构的划分及实现过程 022036930019 张志龙 2022.11.18 题目 什么是网络社团结构&#xff0c;介绍给出社团结构划分几种常见算法&#xff0c;并且给出你实现的过程。同时对一些真实网络进行划分与真实情况进行比较&#xff0c;并且给出你的解释。 文章目录社团结构的划分…

整个寒假挑灯夜读用学习压抑悲伤之情(寒假总结)

目录 前言 一、回顾这一个多月&#xff08;学习阶段&#xff09; 二、意外经历——青训营 三、下学期规划 四、其他 前言 这几年过年越来越没有年味了&#xff0c;所以对过年并没有多大的期待&#xff0c;当别人都在朋友圈发新年快乐的时候&#xff0c;我应该在原神过海灯…

华为OD机试 - 组成最大数(Java) | 机试题算法思路 【2023】

使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看地址:https://blog.csdn.net/hihell/category_12201821.html 华为OD详细说明:https://dream.blog.csdn.net/article/details/128980730 组成最大数 小组中…

maven-surefire-plugin,用于自动化测试和单元测试的

如果你执行过mvn test或者执行其他maven命令时跑了测试用例&#xff0c;你就已经用过maven-surefire-plugin了。 maven-surefire-plugin是maven里执行测试用例的插件&#xff0c;不显示配置就会用默认配置。这个插件的surefire:test命令会默认绑定maven执行的test阶段。 2.ma…

同城小程序应该怎么做?

同城小程序应该怎么做?同城小程序开发&#xff0c;微信同城小程序&#xff0c;同城生活小程序&#xff0c;同城信息发布小程序#同城小程序开发#微信同城小程序#同城生活小程序#同城信息发布小程序百收网 同城信息发布的小程序怎么做&#xff1f; 实际上跟 58 同城类似的&…

SpringBoot整合Dubbo和Zookeeper

安装Zookeeper 下载地址&#xff1a;https://zookeeper.apache.org/releases.html#download 解压&#xff0c;然后运行bin目录里的zkService.cmd 将conf文件夹的zoo_sample.cfg复制一份改名为zoo.cfg 修改zoo.cfg配置&#xff0c;dataDir临时数据存储的位置&#xff0c;client…

5.11 BGP属性-Preferred-Value

5.4.5配置Preferred-Value属性控制选路 1. 实验目的 熟悉Preferred-Value属性控制选路的应用场景掌握Preferred-Value属性控制选路的配置方法2. 实验拓扑 实验拓扑如图5-11所示: 图5-11:配置Preferred-Value属性控制选路 3. 实验步骤…

Python|每日一练|数组|回溯|栈|树|双指针|单选记录:N 皇后|二叉树的前序遍历|四数之和

1、N 皇后&#xff08;数组&#xff0c;回溯&#xff09; n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决方案。 每一种解法包含一个不同的 n 皇后问题 …