工作中的代码设计:01-创建形代码设计

系列前言:

  • 为什么要写这个系列?
    • 这周一个朋友在工作时问我:当我要写一个SDK方法给业务方调用时,那这个代码该怎么设计?我第一反应就是:就是一个很经典的创建型场景。但是这位朋友还是在此基础上花费的比较多的时间。
    • 讲解完该问题后,也想到之前在带人以及code review时,很多人写的代码都是梭哈式的开发,很少有人会去考虑代码的设计模式。
    • 设计模式大家都或多或少的了解,但是在工作中写代码时,往往会忽略设计模式的应用。
    • 所以我想通过这个系列来带大家了解一下设计模式在工作中的应用。
  • 这个系列想怎么做?
    • 通过一个个的场景来讲解设计模式的应用,每个场景都是我在工作中遇到的,或者是我在code review中看到的。
    • 不去说明那28种设计模式,而是通过实际的场景来讲解设计模式的应用。
    • 通过这个系列,希望大家能够了解设计模式的应用场景,以及如何在工作中应用设计模式。

如何封装一个SDK方法

业务场景

  • 微信分享业务。需要接入微信分享的SDK,提供给业务方调用,包括图片分享,视频分享等。
  • 现有的代码是存在一个 sdk_manager,负责管理所有的sdk方法,现在需要在sdk_manager中添加一个微信分享的方法。

场景分析

  • 微信分享流程和接口:

    1. 初始化微信SDK(SDK.Init)。
    2. 构建分享参数。
    3. 调用微信分享接口(SDK.Share)。
  • 从业务调用方来说,业务调用方是不需要知道微信分享的具体流程,只需要调用对应的具体方法就行。

  • 从代码设计来说,我们需要将微信分享的流程封装到一个方法中,提供给业务方调用。

  • 思考的点就是:希望提高代码复用性,还是希望代码更加灵活具有可扩展性。

    • 如果是希望代码复用性高,那么我们可以将微信分享的流程封装到一个方法中,提供给业务方调用。
      • 这里带来的问题是:如果微信分享的流程发生变化,那么我们需要修改这个方法,可能会影响到其他业务方调用。
    • 如果是希望代码更加灵活具有可扩展性,那么我们可以将微信分享的流程拆分成多个方法,提供给业务方调用。
      • 这里就是后续微信增加了新的分享方式,我们只需要新增一个方法即可,不会影响到其他业务方调用。
    • 从代码设计的开闭原则考虑,我们应该选择第二种方式。

代码设计

这里的代码将以Go为例
全部代码可以看仓库源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package builder

// ShareItem 分享内容,给外界构造用的
type ShareItem struct {
Title string
Url string
// 图片分享必传参数
ImageOption string
// 视频分享必传参数
VideoOption string
}

type ShareVideoItemBuilder struct {
Title string
Url string
// 视频分享必传参数
VideoOption string
}

func (b *ShareVideoItemBuilder) SetTitle(title string) *ShareVideoItemBuilder {
b.Title = title
return b
}

func (b *ShareVideoItemBuilder) SetUrl(url string) *ShareVideoItemBuilder {
b.Url = url
return b
}

func (b *ShareVideoItemBuilder) SetVideoOption(videoOption string) *ShareVideoItemBuilder {
b.VideoOption = videoOption
return b
}

func (b *ShareVideoItemBuilder) Build() *ShareItem {
return &ShareItem{
Title: b.Title,
Url: b.Url,
VideoOption: b.VideoOption,
}
}

type ShareImageItemBuilder struct {
Title string
Url string
// 图片分享必传参数
ImageOption string
}

func (b *ShareImageItemBuilder) SetTitle(title string) *ShareImageItemBuilder {
b.Title = title
return b
}

func (b *ShareImageItemBuilder) SetUrl(url string) *ShareImageItemBuilder {
b.Url = url
return b
}

func (b *ShareImageItemBuilder) SetImageOption(imageOption string) *ShareImageItemBuilder {
b.ImageOption = imageOption
return b
}

func (b *ShareImageItemBuilder) Build() *ShareItem {
return &ShareItem{
Title: b.Title,
Url: b.Url,
ImageOption: b.ImageOption,
}
}
GO

调用代码示例如下:

1
2
3
4
imageItem := NewShareVideoItemBuilder().SetVideoOption("videoOption").SetTitle("title").SetUrl("url").Build()
ManagerInstance.DoShareImage(imageItem)
videoItem:= NewShareImageItemBuilder().SetImageOption("imageOption").SetTitle("title").SetUrl("url").Build()
ManagerInstance.DoShareVideo(videoItem)
GO

其实直接看代码就会发现,我们在遇到旧的代码风格存在问题时,我们要先了解这个代码的业务场景,然后再去思考如何设计代码,这样才能更好的解决问题。

这里的代码设计就是一个很经典的创建型设计模式:建造者模式。通过建造者模式,我们将一个复杂对象的构建和表示分离,使得同样的构建过程可以创建不同的表示。

并且保留了旧的代码风格,这样可以让旧的业务方调用不受影响,同时也可以让新的代码后续维护更加方便。

总结

  • 这篇实际上就是一个很经典的创建型设计模式:建造者模式。
  • 说出花来,就是将一个复杂对象的构建和表示分离,使得同样的构建过程可以创建不同的表示。
  • 然后在实际的工作场景中,我们要让旧的业务方调用不受影响,同时也要让新的代码后续维护更加方便,所以最后还是通过ManagerInstance来调用新的方法。

工作中的代码设计:01-创建形代码设计
https://blog.codefish.net/2024/09/20/code-design-job-code-design-01-builder/
作者
CodeFish
发布于
2024年9月20日
许可协议