系列前言:
为什么要写这个系列?
这周一个朋友在工作时问我:当我要写一个SDK方法给业务方调用时,那这个代码该怎么设计?我第一反应就是:就是一个很经典的创建型场景。但是这位朋友还是在此基础上花费的比较多的时间。
讲解完该问题后,也想到之前在带人以及code review时,很多人写的代码都是梭哈式的开发,很少有人会去考虑代码的设计模式。
设计模式大家都或多或少的了解,但是在工作中写代码时,往往会忽略设计模式的应用。
所以我想通过这个系列来带大家了解一下设计模式在工作中的应用。
这个系列想怎么做?
通过一个个的场景来讲解设计模式的应用,每个场景都是我在工作中遇到的,或者是我在code review中看到的。
不去说明那28种设计模式,而是通过实际的场景来讲解设计模式的应用。
通过这个系列,希望大家能够了解设计模式的应用场景,以及如何在工作中应用设计模式。
如何封装一个SDK方法业务场景
微信分享业务。需要接入微信分享的SDK,提供给业务方调用,包括图片分享,视频分享等。
现有的代码是存在一个 sdk_manager,负责管理所有的sdk方法,现在需要在sdk_manager中添加一个微信分享的方法。
场景分析
微信分享流程和接口:
初始化微信SDK(SDK.Init)。
构建分享参数。
调用微信分享接口(SDK.Share)。
从业务调用方来说,业务调用方是不需要知道微信分享的具体流程,只需要调用对应的具体方法就行。
从代码设计来说,我们需要将微信分享的流程封装到一个方法中,提供给业务方调用。
思考的点就是:希望提高代码复用性,还是希望代码更加灵活具有可扩展性。
如果是希望代码复用性高,那么我们可以将微信分享的流程封装到一个方法中,提供给业务方调用。
这里带来的问题是:如果微信分享的流程发生变化,那么我们需要修改这个方法,可能会影响到其他业务方调用。
如果是希望代码更加灵活具有可扩展性,那么我们可以将微信分享的流程拆分成多个方法,提供给业务方调用。
这里就是后续微信增加了新的分享方式,我们只需要新增一个方法即可,不会影响到其他业务方调用。
从代码设计的开闭原则考虑,我们应该选择第二种方式。
代码设计
这里的代码将以Go为例全部代码可以看仓库源码
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071package 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, }}
调用代码示例如下:
1234imageItem := NewShareVideoItemBuilder().SetVideoOption("videoOption").SetTitle("title").SetUrl("url").Build()ManagerInstance.DoShareImage(imageItem)videoItem:= NewShareImageItemBuilder().SetImageOption("imageOption").SetTitle("title").SetUrl("url").Build()ManagerInstance.DoShareVideo(videoItem)
其实直接看代码就会发现,我们在遇到旧的代码风格存在问题时,我们要先了解这个代码的业务场景,然后再去思考如何设计代码,这样才能更好的解决问题。
这里的代码设计就是一个很经典的创建型设计模式:建造者模式。通过建造者模式,我们将一个复杂对象的构建和表示分离,使得同样的构建过程可以创建不同的表示。
并且保留了旧的代码风格,这样可以让旧的业务方调用不受影响,同时也可以让新的代码后续维护更加方便。
总结
这篇实际上就是一个很经典的创建型设计模式:建造者模式。
说出花来,就是将一个复杂对象的构建和表示分离,使得同样的构建过程可以创建不同的表示。
然后在实际的工作场景中,我们要让旧的业务方调用不受影响,同时也要让新的代码后续维护更加方便,所以最后还是通过ManagerInstance来调用新的方法。