【每周小结】2023-Week4
新的一年已经到来,祝各位读者2023年身体健康、家庭美满。
回到本篇的主题,我继续来聊聊本周的一些心得。
Go技巧 - 接口实现下的三个代码复用技巧
在面向对象开发的场景下,我们经常会写高度重复的Go
代码。为了帮助大家形成一定的方法论,这里以一个具体场景为例,分享我的三个技巧。
示例:一个接口 + 三个实现
我们以上图为例,看看示例代码:
// 接口定义 - 订单
// 方法定义:创建订单Create与关闭订单Close
type Order interface {
Create() error
Close() error
}
// 三种具体的订单类型 Order1 Order2 Order3
// 为了实现接口Order,这三个结构都需要实现 Create与Close 方法
// Order1部分
type Order1 struct {}
func (o *Order1) Create() error {
}
func (o *Order1) Close() error {
}
// Order2部分
type Order2 struct {}
func (o *Order2) Create() error {
}
func (o *Order2) Close() error {
}
// Order3部分
type Order3 struct {}
func (o *Order3) Create() error {
}
func (o *Order3) Close() error {
}
在实际场景中,Order1
/Order2
/Order3
的逻辑、数据高度相似,出现大量的重复性代码。如何提升这部分代码的开发效率呢?下面给出三个途径:
方法1:快刀斩乱麻 - 函数复用
最直接的方法就是抛开面向对象的一堆概念,单纯地用函数复用来解决问题:
func createOrder(ctx context.Context, data interface{}) error {
}
func closeOrder(ctx context.Context, data interface{}) error {
}
// 以Order1为例,Order2/Order3类似
type Order1 struct {}
func (o *Order1) Create() error {
// 调用 createOrder
}
func (o *Order1) Close() error {
// 调用 closeOrder
}
这种编程思维是面向过程的,虽然不够抽象,但它确实是 最便捷的代码复用方式。而且,在很多情况下,我们不会对这块代码有大更新,函数复用是一个 高性价比 的选择。
但我们的追求不仅限于此:如果这块代码涉及业务核心,高频迭代,会出现什么样的现象呢?举3个例子:
// 示例1 - 入参不断增加
// 某些订单的需要一些额外的数据,那么就必须增加入参(并且这个参数很难通用!)
func createOrder(ctx context.Context, data interface{}, other, more interface{}) error {
}
// 示例2 - 大量的if-else
// 一个函数适配多种逻辑,只能增加判断逻辑
func createOrder(ctx context.Context, data interface{}, otherData interface{}) error {
if orderType == 1 {
} else if price > 1000 {
} else {
}
}
// 示例3 - 创建多个函数
func createOrder2() error {}
func createOrder3() error {}
以上这些代码是不整洁的,相信大部分人不愿在自己开发过程中看到。这种过程性代码复用的思路,见效虽快,但在复杂场景下弊端愈发明显。下面,我们引入第二个方法:
方法2:对象抽象的妙用 - 嵌套+Overwrite
Go
并不是一门完全面向对象的语言,但对于复杂场景,会用嵌套来支持一定的代码复用。代码示例如下:
// 一个基础对象,实现了接口 Order
type CommonOrder struct {}
func (o *CommonOrder) Create() error {
}
func (o *CommonOrder) Close() error {
}
// Order1 利用嵌套,直接实现了Create和Close两个方法
type Order1 struct {
*CommonOrder
}
// Order2 也利用了嵌套,Close方法会复用,但Create方法会被覆盖
type Order2 struct {
*CommonOrder
}
// Overwrite
func (o *Order2) Create() error {
}
// Order3 两个方法都会被覆盖
type Order3 struct {
*CommonOrder
}
// Overwrite
func (o *Order3) Create() error {
}
// Overwrite
func (o *Order3) Close() error {
}
嵌套+Overwrite
的组合能力,支撑了Go
语言面向对象的很多特性。与之前的函数复用对比,这种方法的可读性会更棒(这也非常依赖开发者面向对象的抽象能力)。
到这一阶段,维护绝大多数的项目已经足够。但如果你是一个苛求细节的人,在继续开发的过程中会发现一个问题:即便代码的逻辑一致,我们却常常因为数据结构不同,而编写出高度重复性的代码。那么,我们再看第三个方法:
方法3:剥离数据结构的差异 - 泛型
我们先看如下代码:
type OrderInfo1 struct{}
func (o *Order1) Create() error {
// 数据结构 OrderInfo1 保存的是 Order1 订单信息
var order *OrderInfo1
// 插入msyql
err := mysql.Insert(order)
if err != nil {
return err
}
// 序列化后打印
b,err := json.Marshal(order)
if err != nil {
return err
}
fmt.Println(string(b))
return nil
}
type OrderInfo2 struct{}
func (o *Order2) Create() error {
// 数据结构 OrderInfo2 保存的是 Order2 订单信息
var order *OrderInfo2
// 后面操作同Order1
}
type OrderInfo3 struct{}
func (o *Order3) Create() error {
// 数据结构 OrderInfo3 保存的是 Order3 订单信息
var order *OrderInfo3
// 后面操作同Order1
}
这部分代码很难通过上述两个方法解决。而如果利用泛型,会变得非常巧妙:
// 将公共逻辑抽象到这个泛型函数中
func Create[OrderInfo interface{}](order OrderInfo) error {
err := mysql.Insert(order)
if err != nil {
return err
}
b,err := json.Marshal(order)
if err != nil {
return err
}
fmt.Println(string(b))
return nil
}
// 三个Order的Create方法就非常清晰了
func (o *Order1) Create() error {
var order *OrderInfo1
return Create[OrderInfo1](order)
}
func (o *Order2) Create() error {
var order *OrderInfo2
return Create[OrderInfo2](order)
}
func (o *Order3) Create() error {
var order *OrderInfo3
return Create[OrderInfo3](order)
}
泛型特性的引入,往往出现在数据处理层,即和基础库、工具库相关的地方,而在业务层很少出现。我们可以从如下两点进行分析:
- 业务层主要的特点在与 逻辑差异大,对数据结构也有各种校验等,不适用泛型;
- 数据处理层则往往逻辑一致,仅仅只有数据结构的差异,泛型非常适配。
小结
函数复用、嵌套+Overwrite
、泛型,是三种非常有效的代码复用技巧。希望大家能够循序渐进,在工程中找到属于自己的最佳实践。
编程思考 - 开发前的三个文档
在开发一个项目前,有三个文档是必备的,我们称为 - BRD、PRD、技术方案,它们在项目流程中依次编写。
- BRD(商业需求文档):这个文档有一个关键词 - 商业价值,不仅要了解用户痛点,更要结合市场,发掘价值
- PRD(产品需求文档):与产品经理角色相关,设计功能交互,体现出两种重要的思维:产品思维与用户思维
- 技术方案:开发者最熟悉的文档,最主要的是设计,但更重要的是评估能力,如排期、风险
编写技术方案不难,普通开发者工作两三年就能有一个很棒的呈现;而PRD则须要 视野转换,从用户与产品的角度来思考功能的开发;BRD则最为复杂,往往要多年行业经验积累以及深刻的用户洞察。
大家可以在日常开发中多主动地接触优秀的PRD、BRD,不仅能拓宽视野,更能提升个人认知。
工作生活 - 学会聚焦,才能做好取舍
不同人、在不同的阶段,对工作和生活的平衡点都有不同的理解。所以,我认为没有必要去过多地从他人经验里去探求 最佳平衡点,也没有必要把工作和生活当作对立面,而是在日常反复问自己:我究竟想要什么?
有取,往往就需要舍弃,这时就会犹豫代价是否过大。我总是过多地担忧所失去的,就扭曲了原问题:不再关注自己最想要的,而转过头去关注可能失去的,情绪上出现焦虑,甚至恐慌。简而言之,就是要认清自己,学会聚焦。
Github: https://github.com/Junedayday/code_reading Blog: http://junes.tech/ Bilibili: https://space.bilibili.com/293775192 公众号: golangcoding
相关文章
- 来了 Photoshop 2023最新版 支持win10/11系统
- Adobe InCopy 2021 2023 软件下载及安装教程(Mac版)
- 一文读懂NodeJs知识体系和原理浅析_2023-03-01
- PhotoShop CC2018安装详细步骤PS全版本软件下载地址(包括最新的2023)
- 西部数据宣布减产30%,2023年资本支出缩减14.8%
- 2023,电商行业将继续向“内”增长
- 【每周小结】2023-Week2
- 【每周小结】2023-Week5
- 【每周小结】2023-Week6
- Sysdig 2023 云原生安全和使用报告
- Photoshop 2023下载安装PS全版本软件下载地址(包括最新的2023)
- 2023我的前端面试小结_2023-03-13
- 字节前端二面react面试题(边面边更)_2023-03-13
- Acrobat Pro DC 2023 for Mac(PDF编辑器)中文版
- 【愚公系列】2023年01月 .NET/C#知识点-EF Core性能优化之显示编译
- 【2023新书】数据科学的数学基础
- 达摩院2023十大科技趋势发布,生成式AI将进入应用爆发期
- 【2023中国数据安全企业全景图谱】调研启动