多分支if else 消除小技巧

问题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 带来的复杂性和臃肿性问题。

策略模式适用于以下场景:

  1. 针对同一类型问题,有多种处理方式,每一种都能独立解决问题。

  2. 需要自由切换选择不同类型算法场景。

  3. 需要闭屏算法具体实现规则场景。

一般要实现一个较为完整的策略模式,需要如下组成单元:

  1. 上下文控制函数:用来拒绝切换不同算法,屏蔽高层模块(调用者)对策略、算法的直接访问,封装可能存在的变化–可以用简单工程与单例模式封装该函数

  2. 抽象要实现的策略接口:定义一个interface,决定好内部包含的具体函数方法定义。

  3. 具体的策略角色:实现每个类实现抽象接口的方法,进行内部具体算法的维护实现即可。

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)
}

总结

优点:

  1. 策略模式符合开闭原则

  2. 避免使用多重条件语句, if…else; switch…case语句

  3. 使用策略模式可以提高算法的保密性和安全性

缺点:

  1. 调用者必须提前约定好对应的调用条件,并自行决定使用哪一个策略类算法。

  2. 代码在一开始写的时候会比较多,在写的时候可能会加大工作量,但是在后续维护添加时简洁清晰、一目了然。

本作品采用《CC 协议》,转载必须注明作者和本文链接
Bergman
讨论数量: 1
kinyou

嗯, 很实用

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!