问题1
都知道大量 if else 对代码维护和设计都及其不友好,即便是你换成 switch 也并不那么理想。
if { | |
... | |
} else if { | |
... | |
} ... { | |
} else { | |
... | |
} |
例子
话不多说,代码搞起来,先来一个可以待优化的实例:
package main | |
import ( | |
"errors" | |
"fmt" | |
) | |
type MessageParams struct { | |
Type string // 消息类型:img=单图,imgs=多图,video=视频 ... | |
Content string // 消息内容 | |
FromUser string // 发送方 | |
ToUser string // 接收方 | |
} | |
func main() { | |
// 发送单图 | |
sendImg := MessageParams{ | |
Type: "img", | |
Content: "发送单图消息", | |
FromUser: "A", | |
ToUser: "B", | |
} | |
// 发送多图 | |
sendImgs := MessageParams{ | |
Type: "imgs", | |
Content: "发送多图", | |
FromUser: "A", | |
ToUser: "B", | |
} | |
// 发送视频 | |
sendVideo := MessageParams{ | |
Type: "video", | |
Content: "发送视频", | |
FromUser: "A", | |
ToUser: "B", | |
} | |
SendMessage(sendImg) // 单图 | |
SendMessage(sendImgs) // 多图 | |
SendMessage(sendVideo) // 视频 | |
} | |
func SendMessage(params MessageParams) error { | |
if params.Type == "img" { // 单图 | |
// 判断用户状态 | |
// 判断用户权限 | |
// 整理发送数据 | |
// 发送消息 | |
// 消息记录入库 | |
fmt.Printf("fromUser [%v] => toUser [%s] : content [%v]\n", params.FromUser, params.ToUser, params.Content) | |
} else if params.Type == "imgs" { // 多图 | |
// 判断用户状态 | |
// 判断用户权限 | |
// 整理发送数据 | |
// 发送消息 | |
// 消息记录入库 | |
fmt.Printf("fromUser [%v] => toUser [%s] : content [%v]\n", params.FromUser, params.ToUser, params.Content) | |
} else if params.Type == "video" { // 视频 | |
// 判断用户状态 | |
// 判断用户权限 | |
// 整理发送数据 | |
// 发送消息 | |
// 消息记录入库 | |
fmt.Printf("fromUser [%v] => toUser [%s] : content [%v]\n", params.FromUser, params.ToUser, params.Content) | |
} else { // 未知 | |
return errors.New("类型不存在") | |
} | |
return nil | |
} |
初步优化
package main | |
import ( | |
"errors" | |
"fmt" | |
) | |
type MessageParams struct { | |
Type string // 消息类型:img=单图,imgs=多图,video=视频 ... | |
Content string // 消息内容 | |
FromUser string // 发送方 | |
ToUser string // 接收方 | |
} | |
func main() { | |
// 发送单图 | |
sendImg := MessageParams{ | |
Type: "img", | |
Content: "发送单图消息", | |
FromUser: "A", | |
ToUser: "B", | |
} | |
// 发送多图 | |
sendImgs := MessageParams{ | |
Type: "imgs", | |
Content: "发送多图", | |
FromUser: "A", | |
ToUser: "B", | |
} | |
// 发送视频 | |
sendVideo := MessageParams{ | |
Type: "video", | |
Content: "发送视频", | |
FromUser: "A", | |
ToUser: "B", | |
} | |
SendMessage(sendImg) // 单图 | |
SendMessage(sendImgs) // 多图 | |
SendMessage(sendVideo) // 视频 | |
} | |
func SendMessage(params MessageParams) error { | |
var err error | |
switch params.Type { | |
case "img": | |
err = RongyunSendImgMessage(params.FromUser, params.ToUser, params.Content) | |
case "imgs": | |
err = RongyunSendImgsMessage(params.FromUser, params.ToUser, params.Content) | |
case "video": | |
err = RongyunSendVideoMessage(params.FromUser, params.ToUser, params.Content) | |
default: | |
return errors.New("类型不存在") | |
} | |
if err != nil { | |
return err | |
} | |
return nil | |
} | |
func RongyunSendImgMessage(fromUser string, toUser string, Content string) error { | |
// 判断用户状态 | |
// 判断用户权限 | |
// 整理发送数据 | |
// 发送消息 | |
// 消息记录入库 | |
fmt.Printf("fromUser [%v] => toUser [%s] : content [%v]\n", fromUser, toUser, Content) | |
return nil | |
} | |
func RongyunSendImgsMessage(fromUser string, toUser string, Content string) error { | |
// 判断用户状态 | |
// 判断用户权限 | |
// 整理发送数据 | |
// 发送消息 | |
// 消息记录入库 | |
fmt.Printf("fromUser [%v] => toUser [%s] : content [%v]\n", fromUser, toUser, Content) | |
return nil | |
} | |
func RongyunSendVideoMessage(fromUser string, toUser string, Content string) error { | |
// 判断用户状态 | |
// 判断用户权限 | |
// 整理发送数据 | |
// 发送消息 | |
// 消息记录入库 | |
fmt.Printf("fromUser [%v] => toUser [%s] : content [%v]\n", fromUser, toUser, Content) | |
return nil | |
} |
解决方案
策略模式
策略模式(strategy pattern) 又叫为政策模式(policy pattern),它将定义的算法家族分别封装起来,让它们之间可以互相切换,让算法的变化不会影响到使用算法的用户,属于 行为型设计模式。
应用场景:
比如:在你发送好友消息的时候
解决在多重相似算法情况下使用了 if…else 和 switch…case 带来的复杂性和臃肿性问题。
策略模式适用于以下场景:
- 针对同一类型问题,有多种处理方式,每一种都能独立解决问题。
- 需要自由切换选择不同类型算法场景。
- 需要闭屏算法具体实现规则场景。
一般要实现一个较为完整的策略模式,需要如下组成单元:
- 上下文控制函数:用来拒绝切换不同算法,屏蔽高层模块(调用者)对策略、算法的直接访问,封装可能存在的变化–可以用简单工程与单例模式封装该函数
- 抽象要实现的策略接口:定义一个interface,决定好内部包含的具体函数方法定义。
- 具体的策略角色:实现每个类实现抽象接口的方法,进行内部具体算法的维护实现即可。
package main | |
import ( | |
"errors" | |
"fmt" | |
) | |
type RongyunSendMessage interface { | |
Send(fromUser string, toUser string, Content string) error | |
} | |
type RongyunSendImgMessage struct{} | |
func (r RongyunSendImgMessage) Send(fromUser string, toUser string, Content string) error { | |
// 判断用户状态 | |
// 判断用户权限 | |
// 整理发送数据 | |
// 发送消息 | |
// 消息记录入库 | |
fmt.Printf("fromUser [%v] => toUser [%s] : content [%v]\n", fromUser, toUser, Content) | |
return nil | |
} | |
type RongyunSendImgsMessage struct{} | |
func (r RongyunSendImgsMessage) Send(fromUser string, toUser string, Content string) error { | |
// 判断用户状态 | |
// 判断用户权限 | |
// 整理发送数据 | |
// 发送消息 | |
// 消息记录入库 | |
fmt.Printf("fromUser [%v] => toUser [%s] : content [%v]\n", fromUser, toUser, Content) | |
return nil | |
} | |
type RongyunSendVideoMessage struct{} | |
func (r RongyunSendVideoMessage) Send(fromUser string, toUser string, Content string) error { | |
// 判断用户状态 | |
// 判断用户权限 | |
// 整理发送数据 | |
// 发送消息 | |
// 消息记录入库 | |
fmt.Printf("fromUser [%v] => toUser [%s] : content [%v]\n", fromUser, toUser, Content) | |
return nil | |
} | |
type MessageParams struct { | |
Type string // 消息类型:img=单图,imgs=多图,video=视频 ... | |
Content string // 消息内容 | |
FromUser string // 发送方 | |
ToUser string // 接收方 | |
} | |
func main() { | |
// 发送单图 | |
sendImg := MessageParams{ | |
Type: "img", | |
Content: "发送单图消息", | |
FromUser: "A", | |
ToUser: "B", | |
} | |
// 发送多图 | |
sendImgs := MessageParams{ | |
Type: "imgs", | |
Content: "发送多图", | |
FromUser: "A", | |
ToUser: "B", | |
} | |
// 发送视频 | |
sendVideo := MessageParams{ | |
Type: "video", | |
Content: "发送视频", | |
FromUser: "A", | |
ToUser: "B", | |
} | |
SendMessage(sendImg) // 单图 | |
SendMessage(sendImgs) // 多图 | |
SendMessage(sendVideo) // 视频 | |
} | |
var Template = map[string]RongyunSendMessage{ | |
"img": new(RongyunSendImgMessage), | |
"imgs": new(RongyunSendImgsMessage), | |
"video": new(RongyunSendVideoMessage), | |
} | |
func SendMessage(params MessageParams) error { | |
if _, ok := Template[params.Type]; !ok { | |
return errors.New("tagID invalid") | |
} | |
return Template[params.Type].Send(params.FromUser, params.ToUser, params.Content) | |
} |
总结
优点:
- 策略模式符合开闭原则
- 避免使用多重条件语句, if…else; switch…case语句
- 使用策略模式可以提高算法的保密性和安全性
缺点:
- 调用者必须提前约定好对应的调用条件,并自行决定使用哪一个策略类算法。
- 代码在一开始写的时候会比较多,在写的时候可能会加大工作量,但是在后续维护添加时简洁清晰、一目了然。