摘要:许多编程语言都支持,通常是动态编程语言用来实现多态的一种方式。在编程中,也常常用这种方式来描述事物。那么不同的编程语言中,是怎么样实现的呢中的先看一个函数有一个函数,传过来一个参数,是可以获取一个链接的资源的。
如果一只动物走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只动物就可以被称为鸭子。
许多编程语言都支持 Duck Typing ,通常 Duck Typing 是动态编程语言用来实现多态的一种方式。
在理解 Duck Typing 前,先看一张图片,这是曾经一度很火的大黄鸭
先问一个比较考三观的问题:图片中的大黄鸭,它是不是一只鸭子呢?
这个问题,得看你从哪个角度去看,如果从人们常识的认知中的角度去看,它显然不是一只鸭子,因为它连最基本的生命都没有。
但是从 Duck Typing 的角度来看,它就是一只鸭子!
Duck Typing 的原话是,走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么它就是一只鸭子。
这个原话是可以灵活理解的,就看我们怎么定义鸭子的行为,我们可以说,能浮在水上游的,黄色的,可爱的就是鸭子,那么,图片中的大黄鸭,它就是一只鸭子!
这就是所谓的 Duck Typing,它只关心事物的外部行为而非内部结构。它并不关心你这只鸭子是长肉的还是充气的。
在编程中,也常常用这种方式来描述事物。那么不同的编程语言中,Duck Typing 是怎么样实现的呢?
1. Python 中的 Duck Typing
先看一个函数:
def download(fetcher): return fetcher.get("http://xxx");
有一个 download 函数,传过来一个 fetcher 参数,fetcher 是可以获取一个 url 链接的资源的。
这个 fetcher 就是一个 Duck Typing 的对象,使用者约定好这个 fetcher 会有一个 get 函数就可以了。
显然这个 download 函数会有以下问题:
运行时才知道传入的 fetcher 有没有 get 函数。那么站在 download 函数的使用者的角度上看,我怎么知道需要给 fetcher 实现 get 方法呢?我不可能去阅读 download 函数的代码,实际情况中,可能 download 函数的代码很长,可能 fetcher 不只要实现 get 方法,还有其它方法需要实现。通常这种情况需要通过加注释来说明。
2. C++ 中的 Duck Typing
C++ 不是动态语言,但是它也能支持 Duck Typing,它是通过模板来支持的。
示例代码:
templatestring download(const F& fetcher){ return fetcher.get("http://xxxx") }
这段代码与 Python 的实现方法类似,这个 fetcher 随便什么类型都可以,只要实现一个 get 方法,就能通过编译。
那么这种实现方法有什么缺点呢,就是,编译时,才知道传入的 fetcher 有没有 get 方法。
但它比 python 好一点了,python 是运行时才知道,C++ 是编译时就知道。
同样,这种情况,还是需要注释来说明。
3. Java 中的类似代码
Java 没有 Duck Typing,它只有类似的代码。Java 的 duck typing :
String download(F fetcher){ return fetcher.get("http://xxxx") }
它同样也用了模板类型。模板 F 必须 extends FetcherInterface ,有了这个限定,就能逼着 download 函数的使用者对 fetcher 实现 get 方法,它解决了需要注释来说明的缺点。
传入的参数必须实现 FetcherInterface 接口,就没有运行时发现错误,编译时发现错误的问题。
但是,它严格上来说不是 Duck Typing 。
如果 download 函数只依赖 fetcher 的 get 方法,而 FetcherInterface 接口必须要实现除 get 方法以外,还有其它方法,那么也要一一实现,非常不灵活。
4. Go 中的 Duck Typing
在 Java 的 Duck Typing 类似代码中,如果 fetcher 参数需要同时实现两个或以上的接口方法时,Java 是没有办法做到的。但 Go 语言可以做到。
type Fetcher interface { Get(url string) string } type Saver interface { Save(content string) } type FetcherAndSaver interface { Fetcher Saver } func download(f Fetcher) string { return f.Get("http://xxxx") } func save(f saver) { f.Save("some thing") } func downloadAndSave(f FetcherAndSaver) { content := f.Get("http://xxxx") f.Save(content) } # 实现者 type MyFetcherAndSaver struct { } func (f MyFetcherAndSaver) Get(url string) string { ... } func (f MyFetcherAndSaver) Save(content string) { ... } func main() { f := MyFetcherAndSaver{} download(f) save(f) downloadAndSave(f) }
这里定义了三个接口,只要有 Get 方法的就是 Fetcher,只要有 Save 方法的就是 Saver,同时有 Get 方法和 Save 方法就是 FetcherAndSaver 。
实现者 MyFetcherAndSaver 并不需要声明它实现了哪些接口,只要它有相关接口的所定义的方法,那么它的实例,就即能作为 Fetcher 接口来使用,又能作为 Saver 接口来使用,也能作为 FetcherAndSaver 接口来使用。
Go 的实现方法相对比较灵活,又不失类型检查。总的来说,特点有:
即能同时实现多个接口
又具有 python , C++ 的 Duck Typing 灵活性
又具有 java 的类型检查。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/75038.html
摘要:许多编程语言都支持,通常是动态编程语言用来实现多态的一种方式。在编程中,也常常用这种方式来描述事物。那么不同的编程语言中,是怎么样实现的呢中的先看一个函数有一个函数,传过来一个参数,是可以获取一个链接的资源的。 如果一只动物走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只动物就可以被称为鸭子。 许多编程语言都支持 Duck Typing ,通常 Duck Typing 是动态编程...
摘要:有一些定制类的特殊方法,如,其中一些具有动态特性的方法可以用来很方便地处理某些动态状况。动态化属性和方法的调用,当调用不存在的属性时,如果存在方法,就会调用方法来尝试获得属性。这种完全动态的调用可以应对一些动态情况,例如实现。 Python有一些定制类的特殊方法,如__str__()、__iter__()、__getitem__(),其中一些具有动态特性的方法可以用来很方便地处理某些动...
摘要:本文已收录修炼内功跃迁之路我们写的方法在被编译为文件后是如何被虚拟机执行的对于重写或者重载的方法,是在编译阶段就确定具体方法的么如果不是,虚拟机在运行时又是如何确定具体方法的方法调用不等于方法执行,一切方法调用在文件中都只是常量池中的符号引 本文已收录【修炼内功】跃迁之路 showImg(https://segmentfault.com/img/bVbuesq?w=2114&h=12...
摘要:类型检测在中提供两种检测类型的方法,但是它们并不总是靠谱。引用类型引用类型使用检测,返回都是。这个时候可以用,它的语法是实例对象构造函数不仅能检测构造对象的构造器,还检测原型链。 类型检测 在js中提供两种检测类型的方法,但是它们并不总是靠谱。 typeof instanceof 下面我们来看看各个类型的应该如何进行检测比较靠谱 1. 基本类型 基本类型(除null之外)的检测使用...
摘要:本文重点协议是中非正式的接口了解抽象基类的基本概念以及标准库中的抽象基类掌握抽象基类的使用方法。三抽象基类的使用通过继承声明抽象基类声明抽象基类最简单的方式是继承或其他抽象基类注意在之间,继承抽象基类的语法是。 导语:本文章记录了本人在学习Python基础之面向对象篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。 本文重点: 1、协议是Python中非正式的接...
阅读 1841·2023-04-25 14:28
阅读 1873·2021-11-19 09:40
阅读 2777·2021-11-17 09:33
阅读 1359·2021-11-02 14:48
阅读 1691·2019-08-29 16:36
阅读 3281·2019-08-29 16:09
阅读 2898·2019-08-29 14:17
阅读 2356·2019-08-29 14:07