图解设计模式
http://tigerb.cn/2021/03/07/patterns-picture/
前言
常常听别人说设计模式不太容易理解,以及学习设计模式到底能帮我们解决什么问题,今天我们就用几张图来看看:
- 设计模式到底是什么?
- 为什么我们需要学习设计模式?
我也写过烂代码
是的,没什么,我也写过烂代码,刚毕业时业务逻辑也会一个函数干到底,只知道能实现功能就可以了。
1 2 3 4 5
| package demo func YourFunc() { // 所有的逻辑代码一股脑写完...... }
|
知道了拆分函数
自然而然知道了需要合理拆分函数。
然后把各个函数组织起来。
面临新的困境
某一天产品的需求需要支持新的场景,发现某一处的代码逻辑有变动需要支持新的场景,怎么办?
- 整个代码拷贝一份?不会有人这么干吧?(其实我还真见过,你们呢😏)
- 把绿色变动的代码块,复制成一个新的函数,修改为新场景使用的函数?
- 把变动的代码再提为两个新函数,一个绿色为老代码,一个蓝色为新场景代码?
上面这种解决问题的方式就是面向过程的编程思想。
我们都在变优秀
随着我们不断的学习,学会使用了面向对象的特性。
以往函数式编程:
1 2 3 4 5 6 7
| package demo // 函数式编程 // 把一个个你以为可以独立的逻辑封住到一个函数里 func YourFunc() { // ...... }
|
面向对象编程:
1 2 3 4 5 6 7 8 9
| package demo // 面向对象编程 // 把不同的逻辑独立成一个对象 type DemoStruct struct{} func (d *DemoStruct) YourFunc() { // ...... }
|
所以,我们如何用面向对象的思想组织上面的代码呢?
答案:继承。
学会了使用继承
定义一个父类,并把差异业务代码抽象为一个抽象函数,其他代码逻辑都实现在父类。
不同的场景定义为不同的子类,子类继承父类,并实现抽象方法(也就是写差异代码)。
是不是很优雅的解决上面的场景的问题。
什么是设计模式?
优雅的解决上面场景问题时,利用面向对象特性的经验总结,就是设计模式。然而在历史的长河中,已经为我们总结了20+的常用设计模式,我们只需要学习和加以灵活运用即可。比如:
这!就是模板模式
还记得上面使用继承的过程吗?其实我们只需要做一件事情,就是经典的模板模式了,是什么?
答案:保证该场景下父类中封装的方法调用过程是稳定不变的,只是其中的方法可能变化。
1 2 3 4
| 灰色:父类 绿色:场景一子类 蓝色:场景二子类 黄色:场景三子类
|
这!就是策略模式
我们把上面代码做些改动:
- 不使用继承。
- 定义一个接口interface类型。
- 变更原抽象方法为调用一个接口interface类型的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package demo // DemoInterface 接口 type DemoInterface interface { DoSomething(ctx *Context) error } var CurrentStrategyInstance DemoInterface func Demo() { //.....逻辑略...... CurrentStrategyInstance.DoSomething(c) //.....逻辑略...... } 灰色:主业务类
|
- 不同的场景定义为一个具体的类,且实现上面的interface。
1 2
| 灰色:主业务类 绿色:场景一DemoInterface的具体实现类
|
1 2 3
| 灰色:主业务类 绿色:场景一DemoInterface的具体实现类 蓝色:场景二DemoInterface的具体实现类
|
1 2 3 4
| 灰色:主业务类 绿色:场景一DemoInterface的具体实现类 蓝色:场景二DemoInterface的具体实现类 黄色:场景三DemoInterface的具体实现类
|
- 最后我们判断不同的场景初始化不同的具体类,再调用即可。
这!就是简单工厂模式 + 策略模式
接着我们把判断不同的场景初始化不同的具体类单独封装起来,这就是简单工厂模式 + 策略模式。
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
| package demo type DemoFactory struct { } // Get 获取实例 func (f *DemoFactory) Get(instanceType string) DemoInterface { switch instanceType { case "DemoA": return &DemoA{} case "DemoB": return &DemoB{} case "DemoC": return &DemoC{} default: panic("不支持的类型") } } // DemoInterface 接口 type DemoInterface interface { DoSomething(ctx *Context) error } var CurrentStrategyInstance DemoInterface func Demo() { //.....逻辑略...... CurrentStrategyInstance = (DemoFactory{}).Get("DemoA") CurrentStrategyInstance.DoSomething(c) //.....逻辑略...... } 灰色(大):主业务类 灰色(小):简单工厂类 绿色:场景一DemoInterface的具体实现类 蓝色:场景二DemoInterface的具体实现类 黄色:场景三DemoInterface的具体实现类
|
这!就是状态模式
假设判断上面使用何种策略不是依赖外部,而是依赖内部状态,则我们调整下代码,则就变成了状态模式。
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
| package demo
var currentStateInstance DemoInterface
func init() { // 定时器更新状态 go func() { for { select { case t := <-time.NewTicker(1 * time.Second).C: // 模拟变成状态 StateA currentStateInstance = setState("StateA") } } }() } // Get 获取实例 func setState(State string) DemoInterface { // 变更状态 switch State { case "StateA": return &StateA{} case "StateB": return &StateB{} case "StateC": return &StateC{} default: panic("不支持的状态") } }
// DemoInterface 接口 type DemoInterface interface { DoSomething(ctx *Context) error }
// type StateA StateB StateC 略 // 模拟 func Demo() { //.....逻辑略...... CurrentStateInstance.DoSomething(c) //.....逻辑略...... } 灰色(大):主业务类 灰色(小):修改内部状态的函数 绿色:场景一DemoInterface的具体实现类 蓝色:场景二DemoInterface的具体实现类 黄色:场景三DemoInterface的具体实现类
|
结语
举了这么多🌰,所以关于:
- 设计模式到底是什么?
- 为什么我们需要学习设计模式?
你有答案了吗?