内容简介:开篇先抛出问题,Go语言算不算是一门面向对象编程语言?要回答这个问题,我查阅了许多的外文资料,发现对于面向对象编程语言,并不存在一个严格的定义。但从实际的角度出发,只要一门语言拥有类,对象的概念,以及提供了对应的语法,就可以用来实现面向对象编程了,所以从这个角度来看,Go语言是可以被认为是一门面向对象编程语言的。
开篇先抛出问题,Go语言算不算是一门面向对象编程语言?
要回答这个问题,我查阅了许多的外文资料,发现对于面向对象编程语言,并不存在一个严格的定义。
但从实际的角度出发,只要一门语言拥有类,对象的概念,以及提供了对应的语法,就可以用来实现面向对象编程了,所以从这个角度来看,Go语言是可以被认为是一门面向对象编程语言的。
那么抛出第二个问题,Go语言有没有实现面向对象的四大特性? 封装,抽象,继承,多态?
这个问题我不准备全部回答,但关于「多态」,Go语言很明确地提供了非常灵活的语法支持。
本篇文章通过 Go 的多态实现思路,来探讨如何理解「多态」,以及如何真正地在项目中使用,而不是随处可见的「小狗,动物,吠」这种虽然浅显易懂但不够实际落地的例子。
一,多态是什么?
多态的定义比较抽象,这里我贴一下百度词条的
多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。
还是用具体的例子来理解吧,Go的fmt包相信大家都用过,其中fmt.Printf()更是常用的函数,我们先跳到fmt包里,去看一眼这个方法
// Fprintf formats according to a format specifier and writes to w. // It returns the number of bytes written and any write error encountered. func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { p := newPrinter() p.doPrintf(format, a) n, err = w.Write(p.buf) p.free() return } // Printf formats according to a format specifier and writes to standard output. // It returns the number of bytes written and any write error encountered. func Printf(format string, a ...interface{}) (n int, err error) { return Fprintf(os.Stdout, format, a...) } 复制代码
可见到,Printf基于Fprintf实现,在Printf中,显式调用了Fprintf(), 并传入了 os.Stdout
我们看一下Fprintf对os.Stdout的使用方式,可见到Fprintf认为os.Stdout实现了io.Writer,所以认为可以调用io.Writer身上应该具有的Write()方法。
这里有两个比较重要的信息,一个是Go语言的接口语法,一个是Duck Typing。
Go语言的接口语法我不深入介绍,就贴一下源码
type Writer interface { Write(p []byte) (n int, err error) } 复制代码
可见Writer接口只具有一个方法,Write(..),实现它很简单,因为接口是面对功能,不面对实现,所以你完全可以写一个类似下面的例子来实现Write()
func Write(p []byte) (n int, err error) { return len(p), nil } 复制代码
好了,这就一个常见的接口语法,只不过在Go语言中,实现一个接口,并不用显式说明,什么是显式说明,举个例子
比如在 PHP 中,如果某个类实现了一个接口,必须显式做出如下声明
class UriResolver implements UriResolverInterface 复制代码
但是在Go语言中,是采用Duck Typing来实现接口实现,什么是Duck Typing?
“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
具体到上面Fprintf的例子,也就是说:只要传进来的参数对象实现了Write()方法,就可以认为它是一个io.Writer。
这对Go实现多态带来了极大的方便,下面我们实现一个简单的例子,写一个Sprintf()
func Sprintf(format string, args ...interface{}) string { var buf bytes.Buffer Fprintf(&buf, format, args...) return buf.String() } 复制代码
在Sprintf中,我们显示调用Fprintf,并且传入的io.Writer实现者是一个bytes.Buffer对象,这个对象不出意外,也实现了Write()。
所以对于多态,我个人的片面理解就是,对于一个通俗功能,有着「统一的调用方式」和「多种形态的实现」。
比如对于图片的存储功能,有着本地存,云端存等不同形态的实现,但它们被调用的方式分别是 local.Save()和Cloud.Save()
二,多态的意义
从上面的例子可以解读出两个现实意义:
1,因为有了Fprintf,在屏幕上打印(Stdout)和字符串生成(bytes.Buffer)可以不用分别实现「打印」这个功能,不用写StdoutPrintf和BufferPrintf,这就有利于代码复用;
2,以后如果想把数据发送到某个文件里,就可以给Fprintf传入一个file.file之类的对象,只要file.file实现了Write(),这就有利于代码拓展;
三, 多态的实现方式
其实在第一点的例子里已经可以看到,上述的接口(php显式声明),Duck Typing(隐式实现)都是多态的实现方式。
如果你是Java,PHP开发,你也可以通过「继承」->「重写方法」来实现多态。
比如 local和cloud都是继承自storage.
local和cloud都分别重写了storage中的save()方法,那么在就可以在如下的调用中实现多态:
<?php Class Storage { public save(string $pictureUrl) { //... } } Class Local extends Storage { public save(string $pictureUrl) { $this->saveToLocal($pictureUrl); } } Class Cloud extends Storage { public save(string $pictureUrl) { $this->saveToOss($pictureUrl); } } function savePicture(Storage $saver, string $url) { $saver->save($url); } $url = '../xxx.jpg'; $local = new Local(); $cloud = new Cloud(); savePicture($local, $url); savePicture($cloud, $url); 复制代码
到此,本篇就结束了。
联系:adiahomes@163.com
参考
- Go程序设计语言 The Go Programming Language
- 百度词条
- en.wikipedia.org/wiki/Duck_t… .
欢迎关注我们的微信公众号,每天学习Go知识
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。