新的一年已经到来,祝各位读者2023年身体健康、家庭美满。
回到本篇的主题,我继续来聊聊本周的一些心得。
在面向对象开发的场景下,我们经常会写高度重复的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
的逻辑、数据高度相似,出现大量的重复性代码。如何提升这部分代码的开发效率呢?下面给出三个途径:
最直接的方法就是抛开面向对象的一堆概念,单纯地用函数复用来解决问题:
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 {}
以上这些代码是不整洁的,相信大部分人不愿在自己开发过程中看到。这种过程性代码复用的思路,见效虽快,但在复杂场景下弊端愈发明显。下面,我们引入第二个方法:
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
语言面向对象的很多特性。与之前的函数复用对比,这种方法的可读性会更棒(这也非常依赖开发者面向对象的抽象能力)。
到这一阶段,维护绝大多数的项目已经足够。但如果你是一个苛求细节的人,在继续开发的过程中会发现一个问题:即便代码的逻辑一致,我们却常常因为数据结构不同,而编写出高度重复性的代码。那么,我们再看第三个方法:
我们先看如下代码:
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、技术方案,它们在项目流程中依次编写。
编写技术方案不难,普通开发者工作两三年就能有一个很棒的呈现;而PRD则须要 视野转换,从用户与产品的角度来思考功能的开发;BRD则最为复杂,往往要多年行业经验积累以及深刻的用户洞察。
大家可以在日常开发中多主动地接触优秀的PRD、BRD,不仅能拓宽视野,更能提升个人认知。
不同人、在不同的阶段,对工作和生活的平衡点都有不同的理解。所以,我认为没有必要去过多地从他人经验里去探求 最佳平衡点,也没有必要把工作和生活当作对立面,而是在日常反复问自己:我究竟想要什么?
有取,往往就需要舍弃,这时就会犹豫代价是否过大。我总是过多地担忧所失去的,就扭曲了原问题:不再关注自己最想要的,而转过头去关注可能失去的,情绪上出现焦虑,甚至恐慌。简而言之,就是要认清自己,学会聚焦。
Github: https://github.com/Junedayday/code_reading Blog: http://junes.tech/ Bilibili: https://space.bilibili.com/293775192 公众号: golangcoding
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有