装饰器模式
Go设计模式总结
基本原则
- 单一原则
每个模块实现的功能要尽可能简单
- 开闭原则
尽量不要改动上版本的代码
- 面向接口开发
面向接口来实现多态开发,而不是面向类来开发
基本汇总
大部分的设计模式在Go中共有下面几种方式实现
- 使用 interface
Go中 interface 还是个万能的数据类型,类似 c 语言的 void 指针
- 用 interface 实现 多态
这个是最基本的也是经常用到的功能,结合 struct 实现
- 多个 interface 和 多个多态结合
大部分设计模式就是通过这个实现的
装饰器模式
装饰器模式,一种不改变原有代码,却能增加功能的设计模式。
闭包简单实现
首先实现一个简单的逻辑
用闭包来实现装饰器
像下面实现了 work01 的业务处理
type Work func()
func work01() Work{
return func() {
fmt.Println("处理 work01 业务逻辑")
}
}
func main() {
work := work01()
work()
}
接着需要添加 work02 的业务逻辑
要在 work01 处理之前处理 work02
这时可以这么处理
func work02(fn Work) Work {
return func(){
fmt.Println("处理 work02 业务逻辑")
// 实现原始功能
fn()
}
}
main 函数变成这样子
这个时候我们就不修改原代码的情况下添加了 work02 业务逻辑,并且是在执行了work02 后才执行 work01
func main() {
work := work01()
work = work02(work)
work()
}
接着需求又要增加 work03 的功能
需要在 work01 之后执行 work03 功能
这时候实现和上面类似
func work03(fn Work) Work {
return func(){
// 实现原始功能
fn()
fmt.Println("处理 work03 业务逻辑")
}
}
main函数现在变成了这个样子
func main() {
work := work01()
work = work02(work)
work = work03(work)
work()
}
这时候就完成了简单的装饰器模式,不改变原代码的情况下增加功能,更多情况以此类推
像上面的例子经常可以用在web开发中,在处理业务之前开启事物,业务处理完毕后提交事物或者回滚事物
接口简单实现
和上面的逻辑类似,不过这里用接口来处理,因为开发中经常用类和接口
同样在第一版本中实现个简单的功能,这里用接口和多态来实现,这个是最基本和最常用的
type Work interface {
Do()
}
type Work01 struct {}
func (w *Work01)Do() {
fmt.Println("处理 work01 业务逻辑")
}
func main() {
var work Work = &Work01{}
work.Do()
}
接着在第二版本中需要增加 work02 功能,在 work01 执行完之后执行 work02
type Work02 struct {
Work
// 定义其他数据
}
func (w *Work02)Do() {
// 实现原始功能
w.Work.Do()
fmt.Println("处理 work02 业务逻辑")
}
// 装饰work02
func Work02Decorator(w Work) Work {
return &Work02{w}
}
main 函数变成这样子
func main() {
var work Work = &Work01{}
work = Work02Decorator(work)
work.Do()
}
接着在第三个版本中增加 work03 功能,执行完 work02 后执行 work03
type Work03 struct {
Work
// 定义其他数据
}
func (w *Work03)Do() {
// 实现原始功能
w.Work.Do()
fmt.Println("处理 work03 业务逻辑")
}
// 装饰work03
func Work03Decorator(w Work) Work {
return &Work03{w}
}
main 函数变成如下这样子
func main() {
var work Work = &Work01{}
work = Work02Decorator(work)
work = Work03Decorator(work)
work.Do()
}
这样就用接口来实现了装饰器模式
电商项目中订单的金额结算案例
一般刚开始开发项目时,比如在第一个版本中,为了快速出成果,一般是比较简单的功能,比如订单金额结算其实就直接是商品价格*数量
但在后面版本迭代中,可能就会出现其他功能,比如运费、优惠券、满减等,而且这些功能可能还比较复杂
一般情况下是尽可能不动上一个版本的代码的,因为上一个版本经过测试上线等,说明是没什么问题的,而如果改动了上一个版本的代码,可能会出现技术上的bug或者业务逻辑上的错误
像这种情况就可以用装饰器模式来实现
首先我们来实现第一版本的简单逻辑,先定义订单数据和支付金额的接口
type Order struct {
// 支付总金额
PayMoney float32
// 商品总金额
Money float32
}
type MoneySum interface {
// 支付金额
Sum(order *Order)
}
接着实现支付功能,逻辑就直接是商品金额*数量了
type OrderPay struct {
// 定义其他数据
}
func (o *OrderPay)Sum(order *Order) {
// 假设 商品价格为100,数量为5
order.PayMoney = 100 * 5
order.Money = 100 * 5
}
mian 函数如下,这就完成了第一版本的金额结算
func main() {
order := Order{}
var moneySum MoneySum = &OrderPay{}
moneySum.Sum(&order)
fmt.Printf("支付总金额为:%v,商品总金额为:%v",order.PayMoney,order.Money)
}
接着在第二版本中增加满减优惠,增加功能如下
type FullMoney struct {
MoneySum
// 定义其他数据
}
func (f *FullMoney)Sum(order *Order) {
// 实现原始功能
f.MoneySum.Sum(order)
// 满减优惠
if order.PayMoney > 200 {
order.PayMoney -= 50
}
}
// 满减装饰
func FullMoneyDecorator(m MoneySum) MoneySum {
return &FullMoney{m}
}
此时 main 如下
func main() {
order := Order{}
var moneySum MoneySum = &OrderPay{}
moneySum = FullMoneyDecorator(moneySum)
moneySum.Sum(&order)
fmt.Printf("支付总金额为:%v,商品总金额为:%v",order.PayMoney,order.Money)
}
这样子就很完美的增加了第二版本的内容
上面的实现是尽可能简单化,实际开发中每个类在各自的包中,实现自个的方法和定义自个的数据
其他的运费、优惠券等以此类推
redis 缓存封装案例
待更