从软件工程的角度比较 Swift、Go 和 Julia,我有了这些发现!( 二 )


这有什么用呢?它能解决访问者模式之类貌似有点别扭的模式 。
Go中的duck类型
当然 , 面向对象编程并不是解决问题的唯一方法 。 Go使用了面向对象编程的元素 , 但尽可能保持简单 。
在Go中 , 你不需要明确标示出对象实现了接口;只要它包含接口中列出的所有方法 , 就自动实现了该接口 。 因此 , 只要拥有类似Write方法的类都实现了Write接口 。

type Writer interface {Write(p[]byte) (n int, err error)}
因此在Go中 , 你可以从已有的库中发明新接口 , 它就会自动实现 , 而不需要专门设计接口 。 这一点非常像Swift 。
但与Swift不同 , 你不能扩展已有类型 。 相反 , Go的方法是使用自由函数为简单接口添加功能 。 举个例子:
func main {filename :="rocket-engine.txt"file, err :=os.Create(filename)if err != nil {fmt.Fprintf(os.Stderr, "Could not create %s because: %v\n",filename, err)os.Exit(1)}deferfile.Closeengine :="RD-180"var thrustfloat64 = 3830fmt.Fprintf(file,"%s has thrust %0.1f\n", engine, thrust)}
这里可以看到 , fmt.Fprintf函数用来向stderr和打开的文件写入格式化后的文本 。 fmt.Fprintf关心的只是第一个参数有没有遵循Write接口 。 你还可以在此基础上构建更多功能 , 从而为所有实现了Write接口的类型创建更复杂的功能 。
这种方法的局限性是 , 你无法为不同的类型创建不同版本的Fprintf 。 在Swift中这样做是可能的 。 在Swift中 , 只需添加一个支持Fprintf的接口扩展 , 就能提供一个默认的实现 , 就像Go中的自由函数一样 , 但是可以为特定的类型提供一个特殊的Fprintf 。 但Go没有办法解决这个问题 , 除非使用并不太优雅的类型切换语句 。
Julia中的多分发
虽然Swift中的面向协议的编程和扩展非常强大 , 但我仍然要说 , Julia的多分发在组织和重用代码方面有更强大的范式 。
下面来解释一下它的工作原理 。 我们假设Go和Swift都有类似于Julia的类型和接口 , 这样方便我们看到其中的区别 。 假设Swift和Go都有一个show函数或方法、IO接口和Any接口 。
在Julia中对表达式求值 , 即可获得一个对象 , 该对象会在REPL(交互式命令行环境)中显示如下:
julia> xs = [3, 4, 8]3-element Vector{Int64}:348
在Julia的提示符下 , 我创建了一个拥有3个元素的数组 , 存储在变量xs中 。 按回车键之后 , Julia输出了该数组的描述 。 在Julia中 , 这是通过调用show方法完成的 。 当命令行需要显示一个对象时 , 就会调用其show方法 。 上例中调用方式大致如下:
show(output, xs)
它有两个参数 。 output表示控制台 , xs是要显示的对象 。 Julia提供的默认实现大致如下:
function show(io::IO, obj::Any)# implementationcode goes hereend
该实现使用内省来确定类型拥有的字段并输出 。 因此 , 如果在Julia中创建自定义类型并初始化 , 就能得到一个合理的默认显示结果 , 如下面的构造函数调用:
julia> struct Pointx::Inty::Intend
julia> p = Point(3, 4)Point(3, 4)
但是 , 我可以通过重载show方法来改变Julia中的显示:
import Base: show
function show(io::IO, p::Point)print(io,"<", p.x, ", ", p.y, ">")end
现在 , 在REPL中显示p , 就会得到不同结果:

特别声明:本站内容均来自网友提供或互联网,仅供参考,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。