go语言中设计模式的使用

设计模式概述

SOLID原则

SRP:单一职责原则(接口职责应该单一,不要承担过多的职责)

1.如果一个模块面向的都是同一类用户(变化原因一致),那么就没必要进行拆分。
2.如果缺乏用户归类的判断,那么最好的拆分时机是变化发生时。

OCP:开闭原则(就是当别人要修改软件功能的时候,使得他不能修改我们原有代码,只能新增代码实现软件功能修改的目的)

1.一个软件系统应该具备良好的可扩展性,新增功能应当通过扩展的方式实现,而不是在已有的代码基础上修改。
在这里插入图片描述

LSP:里氏替换原则

1.子类型必须能够替换掉它们的基类型

ISP:接口隔离原则

1.模块间的关系应该建立在最小的接口集上。

DIP:依赖倒置原则(简单地说,就是说我们应该面向接口编程。通过抽象成接口,使各个类的实现彼此独立,实现类之间的松耦合)

1、高层模块不应该依赖低层模块,两者都应该依赖抽象
2、抽象不应该依赖细节,细节应该依赖抽象
在这里插入图片描述
简单地说:依赖倒置原则告诉我们要面向接口编程。当我们面向接口编程之后,接口隔离原则和单一职责原则又告诉我们要注意职责的划分,不要什么东西都塞在一起。当我们职责捋得差不多的时候,里氏替换原则告诉我们在使用继承的时候,要注意遵守父类的约定。而上面说的这四个原则,它们的最终目标都是为了实现开闭原则。
在这里插入图片描述

创建型设计模式

1.单例模式(饿汉式、懒汉式)

场景:在创建三方服务client时,通常是在服务启动时进行初始化,然后保证全局唯一。

2.建造者(封装复杂对象的创建过程)

优势:

1、将复杂的构建逻辑从业务逻辑中分离出来,遵循了单一职责原则。
2、可以将复杂对象的构建过程拆分成多个步骤,提升了代码的可读性,并且可以控制属性构建的顺序。
3、对于有多种构建方式的场景,可以将 Builder 设计为一个接口来提升可扩展性。
4、Go 语言中,利用 Functional Options 模式可以更为简洁优雅地完成复杂对象的构建。

使用场景:

配置对象。比如创建 HTTP Server 时需要多个配置项,这种场景通过 Functional Options 模式就能够很优雅地实现配置功能。
SQL 语句对象。一些 ORM 框架在构造 SQL 语句时也经常会用到 Builder 模式。比如 xorm 框架中构建一个 SQL 对象是这样的:builder.Insert().Into(“table1”).Select().From(“table2”).ToBoundSQL()
复杂的 DTO 对象。
在这里插入图片描述
就像是同步服务中,同步客户端,实现了同步控制器,同步控制器有(死信控制器,重试控制器),通过withxxx实现,控制器使用

3.工厂方法(不指定对象具体类型的情况下创建对象)

优势

代码的可读性更好。
与客户端程序解耦,当实例化逻辑变更时,只需改动工厂方法即可,避免了霰弹式修改。

使用场景:

对象实例化逻辑较为复杂时,可选择使用工厂方法模式/简单工厂/静态工厂方法来进行封装,为客户端提供一个易用的接口。
如果实例化的对象/接口涉及多种实现,可以使用工厂方法模式实现多态。
普通对象的创建,推荐使用静态工厂方法,比直接的实例化(比如 &Packet{src: src, dest: dest, payload: payload})具备更好的可读性和低耦合。

4.抽象工厂(产品簇创建场景)

优势:

1.产品创建逻辑和业务逻辑分离,符合单一职责原理。
2.具有较高的可扩展性,新增一种产品族实现,只需新增一个抽象工厂实现即可。

使用场景:

1.系统中有产品族,产品有不同的实现,且需要支持扩展。
2.希望产品的创建逻辑和业务逻辑分离。
同步中心的redis,mysql,mongo,interface。都有建立连接、执行命令等方法
在这里插入图片描述

5.原型模式(解决对象复制问题)

原型模式的实现相对简单,可总结为 2 个关键点:

为原型对象定义 Clone 方法,在此方法上完成成员属性的拷贝。
在客户端程序中通过 Clone 来完成对象的复制。

优势:

1.对客户端隐藏实现细节,有利于避免代码耦合。
2.让客户端代码更简洁,有利于提升可读性。
3.方便地复制复杂对象,有利于杜绝客户端复制对象时的低级错误,比如漏复制属性。

6.装饰者(给现有对象/模块新增功能,middleware)

优势:

1.遵循开闭原则,能够在不修改老代码的情况下扩展新功能。
2.可以用多个装饰器把多个功能组合起来,理论上可以无限组合。

使用场景:

1.gin框架的路由的middleware层
2.go中的context包

7.观察者模式(监听状态变更,类似于发布订阅模式)

优势:

1.消息通信双方解耦。观察者模式通过依赖接口达到松耦合;发布-订阅模式则通过 Broker 达到解耦目的。
2.支持广播通信。
3.可基于 topic 来达到指定消费某一类型消息的目的

使用场景:

1.需要监听某个状态的变更,且在状态变更时,通知到监听者。
2.web 框架。很多 web 框架都用了观察者模式,用户注册请求 handler 到框架,框架收到相应请求后,调用 handler 完成处理逻辑。
3.消息中间件。如 Kafka、RocketMQ 等。

8.迭代器模式(用在访问对象集合的场景,能够向客户端隐藏集合的实现细节)

使用场景:

1.对象集合/存储类模块,并希望向客户端隐藏模块背后的复杂数据结构。
2.希望支持客户端自扩展多种遍历方式。

优势:

1.隐藏模块背后复杂的实现机制,为客户端提供一个简单易用的接口。
2.支持扩展多种遍历方式,具备较强的可扩展性,符合 开闭原则。
3.遍历算法和数据存储分离,符合 单一职责原则。

9.访问者模式(解耦数据结构和算法)

使用场景:

1.k8s 中,kubectl 通过访问者模式来处理用户定义的各类资源。
2.编译器中,通常使用访问者模式来实现对语法树解析,比如 LLVM。
3.希望对一个复杂的数据结构执行某些操作,并支持后续扩展。

优势:

1.数据结构和操作算法解耦,符合 单一职责原则。
2.支持对数据结构扩展多种操作,具备较强的可扩展性,符合 开闭原则。

10.代理模式(为一个对象提供一种代理以控制对该对象的访问)

使用场景:

1.远程代理(remote proxy),远程代理适用于提供服务的对象处在远程的机器上,通过普通的函数调用无法使用服务,需要经过远程代理来完成。因为并不能直接访问本体对象,所有远程代理对象通常不会直接持有本体对象的引用,而是持有远端机器的地址,通过网络协议去访问本体对象。
2.虚拟代理(virtual proxy),在程序设计中常常会有一些重量级的服务对象,如果一直持有该对象实例会非常消耗系统资源,这时可以通过虚拟代理来对该对象进行延迟初始化。
3.保护代理(protection proxy),保护代理用于控制对本体对象的访问,常用于需要给 Client 的访问加上权限验证的场景。
4.缓存代理(cache proxy),缓存代理主要在 Client 与本体对象之间加上一层缓存,用于加速本体对象的访问,常见于连接数据库的场景。使用代理模式,在mysql查询前,增加redis缓存查询
5.智能引用(smart reference),智能引用为本体对象的访问提供了额外的动作,常见的实现为 C++ 中的智能指针,为对象的访问提供了计数功能,当访问对象的计数为 0 时销毁该对象。

优点:

可以在客户端不感知的情况下,控制访问对象,比如远程访问、增加缓存、安全等。
符合 开闭原则,可以在不修改客户端和被代理对象的前提下,增加新的代理;也可以在不修改客户端和代理的前提下,更换被代理对象。

11.命令模式(请求参数化、延迟执行、实现 Undo / Redo 操作)

使用场景:

1.事务模式。事务模式下往往需要 Undo 操作,使用命令模式实现起来很方便。
2.远程执行。Go 标准库下的 exec.Cmd、http.Client 都属于该类型,将请求封装成命令来执行。
3.CQRS 架构。微服务架构模式中的一种,通过命令模式来实现数据的异步更新。
4.延迟执行。当你希望一个操作能够延迟执行时,通常会将它封装成命令,然后放到一个队列中。

优点:

1.符合单一职责原则。在命令模式下,每个命令都是职责单一、松耦合的;当然也可以通过组合的方式,将多个简单的命令组合成一个负责的命令。
2.可以很方便地实现操作的延迟执行、回滚、重做等。把不同数据迁移封装为一个一个的命令,提供回滚等。
3.在分布式架构下,命令模式能够方便地实现异步的数据更新、方法调用等,提升性能。

12.备忘录模式(在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外进行保存,以便在未来将对象恢复到原先保存的状态)

使用场景

1.事务回滚。事务回滚的一种常见实现方法是 undo log,其本质上用的就是备忘录模式。
2.系统快照(Snapshot)。多版本控制的用法,保存某一时刻的系统状态快照,以便在将来能够恢复。
3.撤销功能。比如 Microsoft Offices 这类的文档编辑软件的撤销功能。

优势:

1.提供了一种状态恢复的机制,让系统能够方便地回到某个特定状态下。
2.实现了对状态的封装,能够在不破坏封装的前提下实现状态的保存和恢复。

13.适配器模式(让原本因为接口不匹配而无法一起工作的两个类/结构体能够一起工作)

使用场景:

1.将一个接口 A 转换成用户希望的另外一个接口 B,这样就能使原来不兼容的接口 A 和接口 B 相互协作。
2.老系统的重构。在不改变原有接口的情况下,让老接口适配到新的接口。

优势:

1.能够使 Adaptee 和 Target 之间解耦。通过引入新的 Adapter 来适配 Target,Adaptee 无须修改,符合开闭原则。
2.灵活性好,能够很方便地通过不同的适配器来适配不同的接口。

14.桥接模式(将抽象部分和实现部分进行解耦,使得它们能够各自往独立的方向变化)

使用场景:

1.从多个维度上对系统/类/结构体进行扩展,如插件化架构。
2.在运行时切换不同的实现,如插件化架构。
3.用于构建与平台无关的程序适配层。

优势

1.可实现抽象部分与实现解耦,变化实现时,客户端无须修改代码,符合开闭原则。
2.每个分离的变化点都可以专注于自身的演进,符合单一职责原则。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值