设计模式之策略模式

所谓“条条道路通罗马”,在现实中,为达到某种目的往往不是只有一种方法。比如挣钱养家:可以做点小生意,可以打分工,甚至还可以是偷、抢、赌等等各种手段。在程序语言设计中,也会遇到这种类似的情况,要实现某种功能可以有多种方案选择。比如,在很多地图类软件上,你想从A地点到达B地点时,为你提供几种交通工具的选择,可以查看每种交通工具的所需费用和时间。在程序语言设计中,把这种设计模式叫做策略模式。

策略模式的定义:策略模式是指对一系列的算法定义,并将每一个算法封装起来,而且使它们还可以相互替换

主要解决问题:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护

从上面两点可以知道,策略模式是通过定义一系列的算法,并对其进行封装,使其可以相互替换使用,目的是消除多层if…else语句嵌套的问题。这里的定义一系列的算法的意思是:你可以将每一种方案封装成函数或者是类的形式

策略模式的实现

先看一个日常的例子,根据明天的天气状况,决定明天该做什么:晴天,和女朋友出去逛街;阴天,和隔壁老王打牌;雨天,在家看看电视剧。代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
function doSomething (weather) {
if (weather === '晴天') {
console.log('和女朋友出去逛街')
}

if (weather === '阴天') {
console.log('和隔壁老王打牌')
}

if (weather === '雨天') {
console.log('在家看看电视剧')
}
}

时间流逝,第二天到来了,看了下天气预报今天是阴天,于是查下今天该干什么:

1
2
doSomething('阴天')
// 和隔壁老王打牌

这段代码完全正常,使用起来一点问题都没有,但不易于维护。把天气状况在细分,如:小雨、大雨、雷雨、多云。那岂不是要写一大堆的if语句,这时,doSomething函数就会变得很臃肿,难以维护。

用策略模式重构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 晴天
class Sunny {
constructor () {}

doSomething () {
console.log('和女朋友出去逛街')
}
}

// 阴天
class Cloudy {
constructor () {}

doSomething () {
console.log('和隔壁老王打牌')
}
}

// 雨天
class Rainy {
constructor () {}

doSomething () {
console.log('在家看看电视剧')
}
}

// 定义备忘录类
class Memo {
constructor() {
this.strategy = null
}

setStrategy (strategy) {
this.strategy = strategy // 设置对应的策略对象
}

doSomething () {
this.strategy.doSomething()
}
}

var memo = new Memo()

memo.setStrategy(new Cloudy())
memo.doSomething()

这是模拟传统语言实现的策略模式,用策略模式实现后代码增加了不少,这样有必要吗?从代码量的角度看,策略模式没什么优势。但现实是,作为开发者我们有80%的时间是在维护旧的代码,剩下的20%才是写新的代码,所以写出可维护的代码同样很重要。改用策略模式后的代码虽然代码量增加了,但更易于维护了。为什么更容易维护了呢?因为现在可以很方便的增加或删除天气类型。那个更易于维护一眼便知。

JavaScript策略模式的实现

JavaScript语言没有类的概念,所以上面不是正在意义上的JavaScript语言策略模式。在JavaScript中,函数是一等对象,可以将函数当作一个变量传递到函数内部执行,所以JavaScript语言是天生自带策略模式的哦!用高阶函数实现JavaScript语言的策略模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 晴天
var sunny = function () {
console.log('和女朋友出去逛街')
}

// 阴天
var cloudy = function () {
console.log('和隔壁老王打牌')
}

// 雨天
var rainy = function () {
console.log('在家看看电视剧')
}

// 执行策略事件
function doSomething (something) {
something()
}

doSomething(strategy.cloudy)

是不是眼前一亮?JavaScript语言的策略模式比传统语言,显得更加简洁、更加灵活。

总结

策略模式是一种简单的设计模式,它的设计目的就是为了解决 if…else… 所带来的复杂和难以维护的问题。在实际开发中,如果遇到复杂的 if…else… 语句,那么应该考虑是否适用策略模式去解决。