问题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语句
- 使用策略模式可以提高算法的保密性和安全性
缺点:
- 调用者必须提前约定好对应的调用条件,并自行决定使用哪一个策略类算法。
- 代码在一开始写的时候会比较多,在写的时候可能会加大工作量,但是在后续维护添加时简洁清晰、一目了然。