请教各位 Golang Go语言大佬一个问题
go 语言中的接口与方法的区别
结构体 A ,拥有方法 run
结构体 B ,拥有方法 run
接口 C ,内部有一个方法 run
那么我调用 run 方法时候,可以直接 A.run ,或则 B.run 不就行了
为何还要定义一个接口变量,然后实例化这个变量,再调用方法?这不是多此一举吗?伪代码如下
var x C
x=A{}
x.run
x=B{}
x.run()
我直接
x :=A{}
x.run()
或者
x :=B{}
b.run()
感觉弄一个接口不是多此一举吗?
初学 go ,往大家多多解惑
请教各位 Golang Go语言大佬一个问题
更多关于请教各位 Golang Go语言大佬一个问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
假如其他地方要使用到 run ,那只要参数定义为 C ,就可以根据实际情况使用 A 或 B 而不用修改参数了
更多关于请教各位 Golang Go语言大佬一个问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
type DoI interface { func run() }
type DoSome struct { func run() {} }
func ToDO(obj DoI) {}
ToDO(&DoSome{})
想想适配器的设计模式
多态啊,唐老鸭,北京烤鸭,可达鸭,白马会所头牌鸭 都是鸭子。但是细分不同,所以需要通过接口统一规范,屏蔽掉底层实现逻辑的不同,统一使用一致的方法。
再比如说 io 接口就是非常的好解释。 只要能实现 read write 都可以作为 io 参数传递,所以 网络是 io, 文件也是 io, 内存也是 io 。 其实这是面向对象的一些知识点,如果做过 java 或者 php 就会对这一块比较了解了
我也刚看到这个,一样的疑惑,不过我觉得可能是一种约束,如果能实现这个接口就必定能用接口的方法,一个高层一点的抽象
这个还是为了多态,最简单的一个例子,假设你有一个动物基类,拥有一个 run 方法,然后派生出了 n 个动物类(比如猫、狗、猴子等等),然后有一个函数,函数需要传入一个动物类,并在函数体内调用对应的 run 方法。这个时候,如果你不使用接口,则需要为每一个动物类都定义一个这样的方法,会产生大量冗余代码。
谢谢各位的指导,我也再去想想,只是感觉接口存在是为了管理方法而存在,在实际应用时候,还没搞懂到底在那些场景下使用
实际使用中的例子,win 和 linux 下读取文件列表可以抽象一个 list()方法,在不同地方使用的时候不需要判断是 a.list 还是 b.list 直接 x.list 至于判断在封装的时候处理一次就行。
如果是简单的情况可以像你那样直接弄,确实没必要再封一层。
谢谢,我去体会下,可能使用的少,不是很明白
lz 你学 go 之前是一直在用动态类型语言吗( JS ,Python 之类的),如果是习惯了动态语言的,的确一开始即非常难以理解接口的用处的
接口的存在使得调用 run 的的上下文中不需要确切知道被调用者的具体类型,只需要知道被调用者满足接口约定,就可以使用接口所定义的行为。
感觉接口像是类的实例化,但又不完全是,不知道我理解的对不对
有什么可以帮助理解的方法或者代码例子吗?
#13
> 感觉接口像是类的实例化
说得不对,接口是 OOP 相关的概念,你可能需要相关的前置知识。
需要补一补那块知识,我去学习下
这个其实不是 go 的问题,是 OOP 的概念,可以看看 设计模式
我理解接口最大的用处是解耦,面向接口编程,而不关心具体实现
比如有个任务系统 TaskService ,在某些条件下需要发消息,然后 PushSerivce 给出了一个接口 push(param),提供了消息推送的能力
这样 TaskSerivce 就只需要关心内部的逻辑,至于推送,不用关心细节,只要知道有个 push 接口,直接调就行。
而对于 PushSerivce ,他只提供了一个标准接口,只要保证接口功能和定义正常,底层实现可以随意更换,比如换运营商,换推送渠道(短信、APP ),或者根据入参,路由到不同的实现类上(策略模式)
这里的 PushService 就是你说的 C ,针对不同运营商、渠道的实现就是 A 和 B
直接在这里讲好了
很正常的,编程先接触动态语言的话就会很难理解静态语言的接口到底有什么意义,ls 很多人说的其实都对,但都是站在静态语言使用者的角度在教你,你作为一个动态语言接触编程的人是没法轻松理解他们到底在说什么的。因为接口提供的动态能力是动态语言天生就有的,最先接触动态语言的话会把这种动态能力当成呼吸一样理所当然,所以没法理解静态语言这么大费周章是图什么
所以要跟你讲清楚接口,那就要从 python 角度来帮你理解 —— 在 python 中(虽然 python 没接口),接口就相当于在你调用对象的方法时加了一道验证,限制了过于自由类型系统
比如很下面这段 Python 代码是可以直接跑的,但显然会执行出错对不对,因为 123 和 “Hello” 并没有实现 run()方法<a target="_blank" href="http://main.py" rel="nofollow noopener">main.py</a><br># 假设 A 和 B 是 class 并实现了 run() 方法<br>l = [ A(), B(), 123, "Hello" ] <br><br>for v in l:<br> <a target="_blank" href="http://v.run" rel="nofollow noopener">v.run</a>()<br><br>
上面这问题虽然看起来一目了然,但在复杂的代码中是非常容易出现的(代码复杂了你没法保证会不会脑子抽了随便塞个奇怪的变量到上面的 list 里)
那好,你一定就会想,有没有一套聪明办法,让我能限制 l 这个列表只能放实现了 .run()
方法的变量?比如像下面这样的伪代码,我自创了一个方法集 RunRunRun:<br># 定义一个方法集合<br>method_set RunRunRun: <br> run()<br><br><br>l[ RunRunRun ] = [ A(), B(), 123, "Hello" ] <br><br># 我这里随便自创了一种写法,指定了列表 l 只能存放实现了方法集 RunRunRun 的变量<br># 因为 123 和 "Hello" 没有实现方法集 RunRunRun 里的所有方法,所以这行代码在编写阶段就可以被 IDE 检测出来,并且在编译阶段直接就能报错。而用不着每次到代码跑起来崩了才注意到<br>
在上面段伪代码中,方法集(method_set)的另一个通用的叫法就是接口(interface)。这就是接口的用处。这就是站在动态语言角度来理解接口,接口实际上就是在限制动态语言里过于自由的类型赋值。之所以上面很多人说得你不太理解,其实是站在静态语言角度在给你讲。如果你还不太理解的话,就像上面这样理解接口就行了。
我有点异端,我不认为 Go 的接口是 OOP 的设计。Go 的接口在我看来和其他 OOP 语言的最大区别是,你不需要声明你“实现”了一个接口,只要这个类有这个方法,它就是这个接口。这种方式我认为这是更“自然”,更符合软件开发特点的,用 Rob Pike (Go 创始人之一)的用语就是 organically 。你可以去看看 fmt.Fprintf 的使用就是一个典型例子,当你自己的类实现了一个 io.Writer 的接口,你就自然而然地可以使用这个方法,而整个过程你甚至不需要显式 import io 这个包。
这样就可以把依赖的 A 或者 B 替换成实现了接口 C 的 D 来进行单元测试了
非常感谢你的详细解答,我看了你的解答后好像明白点了,接口作用就是限制和约束类型
通用性
type notify interface {
send()
}
// Email 实现 1
type Email struct{}
func (Email) send() {
fmt.Println(“email send”)
}
// Qq 实现 2
type Qq struct{}
func (Qq) send() {
fmt.Println(“qq send”)
}
func Test(n notify) {
n.send()
}
假如说我现在需要实现一个通知的功能,但是我并不想具体采用哪种方式发通知(比如,微信、电子邮箱、QQ ),所以我就实现一个 notify 接口,它是通知的抽象(并没有具象到具体的采用哪放方式来实现通知)。接下来我利用 Emali 和 Qq 来对这个 notify 接口进行了实现,它们是这个抽象的具象。那么在外部调用的时候,我并不需要关心我具体使用哪种通知方式来发起通知,我只需要实现通知(就好像,老板通知你完成某一件事情,但是老板并不需要知道你完成某件事情的过程,采用哪种方式,他只想要这件事情的结果,而 Email 是同事 A 的完成方式,Qq 是同事 B 的完成方式,最后老板采用哪位同事来处理这件事情,是由老板自己决定,老板只需要指定某个同事就可以了)。即
func main() {
person.Test(&person.Qq{})
}
或
func main() {
person.Test(&person.Email{})
}
Python Oop => poop
duck type
多态、面向抽象而不是实现、接口和实现的解耦等等,写个需要设计的轮子就懂了
> 接口作用就是限制和约束类型
正相反。
静态类型语言的变量,类型是「唯一的」。你可能一下 get 不到有多唯一,我举个自己初学 oop 时对我帮助很大的例子
- 你的程序有两个窗口
- 窗口 A 叫 A ,B 叫<嘎>
- 你给窗口 A 声明了一个类型 type WindowA struct{ Title: A }
- 窗口 B 的类型 type WindowB struct{ Title: <嘎> }
现在你想判断鼠标点击了哪个窗口:
func findWhichClicked(x,y int) (T){}
问题来了: 应该返回什么类型?返回 A ,那么点到 B 这个函数就给不出数据了,反之亦然。
那……返回他们的公共类型?
type WindowA{Window; Title }
type WindowB{Window; Title }// AB 都来自 Window
func findWhich(x,y int) (w Window){}
但还是有问题: 我想获得点到的窗口的标题:
w := findWhich()
w.Title() // ?
做不到!
我永远只能获得 Window 的标题, 不能获得 WindowA 或者 WindowB 的标题。
「这事简直跟我是男人了就不是人了一样荒唐」!!
你可能会疑惑了,那用个公共变量呗
if inA() {w=windowA}
else if inB(){w=windowB} // 类型不匹配!,无法将 WindowA 类型的变量赋值给 Window 类型
------
接口:
type <有 title> interface {Title() }
func findWhichClicked(x,y int) <有 title> {} // windowA 和 windowB 都可以实现 <有 title>
w.Title() // w 可以是任何 <有 title>类型,运行期决定
如果 w 是 windowA ,那么调用 windowA 的实现,是 B 调用 B 的实现。
------
想想这句话: 「我要使用一个变量,它可能代表不同的东西,但类型又得是唯一的」
优点:
1 后期维护方便,可以针对接口扩展实现不同的实例
2 减少 package 之间的依赖,避免循环引用,大型项目中很有用
这不就是多态?
其实一般是为了单元测试 mock 才这么搞……如果不强调单元测试覆盖率,倒也不用处处这么玩。
你有两个结构体,一个是程序,叫 program ,另一个是人,叫 human ,他们都有个方法,.Run()
有个接口叫 Runner ,他定义了一个方法叫做 .Run(),所以 program 和 human 都实现了这个接口
现在有个函数叫做 代码太烂怎么办,WhatIfTheCodeBad(runner Runner),参数是 接口 Runner ,你传 program 和 human 都可以,因为他们都能跑 (实现了 Runner 接口)
你好,很高兴看到你对Go语言感兴趣并提出问题。作为IT领域专注于Go语言的一名开发者,我很乐意尽我所能帮助你。
Go语言,又称为Golang,是一种由谷歌开发的开源编程语言,以其简洁、高效和并发处理能力强而著称。它在云计算、微服务、网络编程等领域有着广泛的应用。
关于你的问题,由于你没有具体说明遇到的是哪方面的问题,我只能提供一些常见的学习和开发建议:
- 基础学习:如果你是初学者,建议从Go语言的官方文档和教程入手,了解其基本语法和核心概念。
- 实战项目:通过参与实战项目,可以加深对Go语言的理解和应用。尝试编写一些小程序或参与开源项目,锻炼自己的编程能力。
- 并发编程:Go语言在并发编程方面有着独特的优势,建议深入学习Go的并发模型(如goroutine和channel),以充分利用其性能优势。
- 社区资源:加入Go语言的社区,如GitHub、Stack Overflow等,与其他开发者交流心得,解决遇到的问题。
当然,如果你有更具体的问题或需求,比如关于某个库的使用、性能优化等方面的问题,请详细描述一下,我会尽我所能给出更具体的解答。
希望这些建议对你有所帮助,祝你在Go语言的学习和开发中取得更大的进步!