设计模式-策略模式(Go语言描述)

qibin0506 · · 2616 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

好久没有更新博客了,最近也是在忙着充电,从今天这篇博客开始,我们来了解一下设计模式。

设计模式

那什么是设计模式呢?首先来看看我从百科上copy下来的概念吧。

设计模式/软件设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

恩,大概的意思就是在撸码界由码神们总结的、能够解决软件设计中反复出现的问题的、由大多数人任何的设计方式,也可以说是一种编码规则。
初始编码我们总是按照自己的套路来写代码,等项目的代码量庞大了,需求又要改了,突然发现尼玛这个改起来好麻烦,好多文件要动,为了适合新的需求改动这个文件可能会对其他的业务逻辑产生影响,改动这个文件会不会引入bug?一系列的问题摆在了我们眼前,头疼、迷茫,有木有一种重新写一遍的冲动?好了,假如你遇到了这种情况,设计模式可能会拯救你一部分。

但,对于设计模式还有很多人更多沉迷于设计模式中,每写几行代码,都要考虑考虑-这里是不是需要个啥模式! 记住,模式是为了方便我们解决问题而总结出来的一套编码规则,并不是我们不用设计模式代码的扩展性就一定差,选择使用模式还是要慎重,大多数情况下还是要依靠需求的情景来考虑需不需要这种模式,在决定使用之前一定要考虑好为什么要用这种模式。

策略模式

好了,扯了这么多蛋,下面开始进入主题,进来我们首先来介绍一下策略模式。首先来看一下定义,

定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化。

恩,这个定义看起来还是可以的,不像一些定义非得整的人看不懂才叫定义,随便还算好理解,不过我们还是要引入一个结构图来更加清晰的认识它。

看到这个结构图,大概可能也许会恍然大悟了,这不就是让我们针对接口编程了,恩是的,差不多所有的设计模式都是要遵循针对接口编程这个规则,而且我们仔细想一下,这里面还体现出了对扩展开发,对修改关闭(开闭原则) ,如果我们要增加一些规则,完全不用修改主业务流程,只需要增加几个策略就ok,当然了,这些策略要遵循我们的接口规范。

代码实现

下面我们就开始以代码的形式来展示一下策略模式吧,代码很简单,我们用一个加减乘除法来模拟。

首先,我们看到的将会是策略接口和一系列的策略,这些策略不要依赖高层模块的实现。

package strategy
/**
 * 策略接口
 */
type Strategier interface {
    Compute(num1, num2 int) int
}

很简单的一个接口,定义了一个方法Compute,接受两个参数,返回一个int类型的值,很容易理解,我们要实现的策略将会将两个参数的计算值返回。
接下来,我们来看一个我们实现的策略,

package strategy
import "fmt"

type Division struct {}

func (p Division) Compute(num1, num2 int) int {
    defer func() {
        if f := recover(); f != nil {
            fmt.Println(f)
            return
        }
    }()

    if num2 == 0 {
        panic("num2 must not be 0!")
    }

    return num1 / num2
}

为什么要拿除法作为代表呢?因为除法特殊嘛,被除数不能为0,其他的加减乘基本都是一行代码搞定,除法我们需要判断被除数是否为0,如果是0则直接抛出异常。
ok,基本的策略定义好了,我们还需要一个工厂方法,根据不用的type来返回不同的策略,这个type我们准备从命令好输入。

func NewStrategy(t string) (res Strategier) {
    switch t {
        case "s": // 减法
            res = Subtraction{}
        case "m": // 乘法
            res = Multiplication{}
        case "d": // 除法
            res = Division{}
        case "a": // 加法
            fallthrough
        default:
            res = Addition{}
    }

    return
}

这个工厂方法会根据不用的类型来返回不同的策略实现,当然,哪天我们需要新增新的策略,我们只需要在这个函数中增加对应的类型判断就ok。

现在策略貌似已经完成了,接下来我们来看看主流程代码,一个Computer,

package compute

import (
    "fmt"
    s "../strategy"
)

type Computer struct {
    Num1, Num2 int
    strate s.Strategier
}

func (p *Computer) SetStrategy(strate s.Strategier) {
    p.strate = strate
}

func (p Computer) Do() int {
    defer func() {
        if f := recover(); f != nil {
            fmt.Println(f)
        }
    }()

    if p.strate == nil {
        panic("Strategier is null")
    }

    return p.strate.Compute(p.Num1, p.Num2)
}

这个Computer中有三个参数,Num1Num2当然是我们要操作的数了,strate是我们要设置的策略,可能是上面介绍的Division,也有可能是其他的,在main函数中我们会调用SetStrategy方法来设置要使用的策略,Do方法会执行运算,最后返回运算的结果,可以看到在Do中我们将计算的功能委托给了Strategier

貌似一切准备就绪,我们就来编写main的代码吧。

package main

import (
    "fmt"
    "flag"
    c "./computer"
    s "./strategy"
)

var stra *string = flag.String("type", "a", "input the strategy")
var num1 *int = flag.Int("num1", 1, "input num1")
var num2 *int = flag.Int("num2", 1, "input num2")

func init() {
    flag.Parse()
}

func main() {
    com := c.Computer{Num1: *num1, Num2: *num2}
    strate := s.NewStrategy(*stra)

    com.SetStrategy(strate)
    fmt.Println(com.Do())
}

首先我们要从命令行读取要使用的策略类型和两个操作数,在main函数中,我们初始化Computer这个结构体,并将输入的操作数赋值给ComputerNum1Num2,接下来我们根据策略类型通过调用NewStrategy函数来获取一个策略,并调用ComputerSetStrategy方法给Computer设置上面获取到的策略,最后执行Do方法计算结果,最后打印。

就是这么简单,现在我们在命令行定位到main.go所在的目录,并执行一下命令来编译文件

go build main.go

继续执行命令

main -type d -num1 4 -num2 2

来尝试一下使用加法策略操作4和2这两个数,来看看结果如何,

结果很正确,换一个策略试试,来个乘法吧,执行命令

main -type m -num1 4 -num2 2

结果也是正确的。

总结

策略模式还是算比较容易理解的,策略模式的核心就是将容易变动的代码从主逻辑中分离出来,通过一个接口来规范它们的形式,在主逻辑中将任务委托给策略。这样做既减少了我们对主逻辑代码修改的可能性,也增加了系统的可扩展性。一定要记得哦,我们的代码要往对扩展开发,对修改关闭这条设计原则上努力!

最后是本文的实例代码下载:http://download.csdn.net/detail/qibin0506/9415084


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:qibin0506

查看原文:设计模式-策略模式(Go语言描述)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2616 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传