通向Golang的捷径【17. 编程模式】

17.1 comma, ok

在之前的章节中, 数次使用了 comma, ok 模式, 一个表达式可给出两个返回值, 第一个返回值可为数值或 nil,第二个返回值可为 true/false, 或是一个错误码, 在 if 条件中, 可给出初始化操作, 并能直接对操作结果进行测试 (即第二个返回值), 从而实现更简洁和优雅的代码, 这是 Go 代码中很重要的一类编程模式, 以下将总结它的用法:

• 对函数返回值进行测试, 参见 5.2 节
在这里插入图片描述
如果函数执行中出现错误, 可将错误码返回给调用者, 如果函数执行成功,err 也可返回一个 nil:
在这里插入图片描述
相同的模式也可用于延期执行 (defer) 的故障 (panic) 恢复 (recover), 参见 17.2/17.4 节, 使用独立的错
误检查函数, 可实现简洁的错误检查, 参见 16.10.2 节.

• 在 map 结构中, 检查键值对 (key-value) 是否存在, 参见 8.2 节, 即 map1 是否包含了一个带数值的 key1:
在这里插入图片描述
• 类型断言: 检查接口变量 varI 的类型是否为 T, 参见 11.3 节.
在这里插入图片描述
• 检查并发通道 ch 是否关闭, 参见 14.3 节.
在这里插入图片描述

17.2 defer

使用 defer 语句, 可保证所有资源能被正确关闭, 或是不再需要的资源可返回给资源池, 并能包含故障的恢复操作.

• 关闭文件数据流,12.7 节
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
同时也可用于报告工作状态.

17.3 可见性

在 4.2.1 节中, 给出了简单的可见性原则, 即 Go 语言中函数, 变量和类型的访问模式, 而在 10.2.1 节中, 给出了如何在工厂函数中, 使用其他包所定义的类型.

17.4 操作符模式和接口

操作符是指一个可返回新对象的函数 (可实现一元或二元运算), 同时函数的形参无法改变, 比如 +,* 符号, 在C++ 语言中, 使用特殊的插入符号 (比如 +,-,* 等) 实现操作符的重载, 除了极少数情况下,Go 语言并不支持操作符重载, 因此操作符运算必须使用函数来实现, 所以 Go 语言给出的过程调用, 等同于面对对象的策略, 同时还包含了两种选择.

17.4.1 函数实现

包内的操作符, 可使用函数实现, 它能传入一个或两个参数, 并给出返回结果, 在包内实现的函数只能用于特定的对象, 比如 matrix 包需要给出矩阵的处理函数, 即矩阵加法 Add(), 矩阵乘法 Mult(), 而这些运算的结果也是一个矩阵, 同时在这些函数的调用中, 应当给出包名, 即
在这里插入图片描述
为了满足不同矩阵 (稀疏矩阵 (sparse), 密集矩阵 (dense)) 的操作, 由于不存在函数重载, 因此必须给出不同的函数名:
在这里插入图片描述
这当然不够优雅, 最好的方式就是能隐藏这些私有函数, 并将它们合并成一个公共函数 Add(), 同时还可使用一个嵌套的类型 switch 语句, 对传入实参的类型进行检查:
在这里插入图片描述
上述代码实现运算操作将会更加优雅, 并在标准库中大量使用, 至于线性代数的运算包 (来自 Ryanne Dolan的贡献), 可参考页面http://code.google.com/p/gomatrix/.

17.4.2 方法实现

不同方法之间的区别在于它的接收器类型, 因此无须使用不同的函数名, 就能为每个类型, 定义一个 Add 方法:
在这里插入图片描述
能返回一个新对象的方法, 可变成下一个方法调用的接收器, 从而实现链式调用:
在这里插入图片描述
而正确的实现方式, 则是基于一个类型 switch 语句, 实现接收器类型的动态 (运行时) 判断:
在这里插入图片描述

17.4.3 接口的用法

当不同的类型中包含了相同的方法, 则可以创建一个通用接口, 以实现所需的多态性. 以下代码定义了 Algebraic 接口:
在这里插入图片描述
因此可为矩阵类型, 定义 Add(),Min(),Mult() 等方法, 每个类型都可实现一个 Algebraic 接口, 以实现方法
的链式调用, 而每个方法中, 都应当使用一个类型 switch 语句, 基于传入实参的类型, 给出对应的处理, 另外default 子句可依赖于接口的特殊方法.
在这里插入图片描述
如果接口方法无法提供一个通用实现, 也就无法提供与类 (class) 相似的功能, 同时操作符模式也将被弃用, 比如 a 是一个集合, 而 b 是一个矩阵, 则无法实现 a.Add(b), 其中的难点在于, 为集合和矩阵两种类型, 实现一个通用 (兼容) 的 a.Add(b), 在这种情况下, 只能在包内, 分别定义两种接口 AlgebraicSet 和AlgebraicMatrix.

在这里插入图片描述