定义
Strategy 策略形式
定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该形式使得算法可独立于运用它的客户程序而变化。
动机
-
在软件构建过程中,某些目标运用的算法或许多种多样,经常改动,假如将这些算法都编码到目标中,将会使目标变得反常复杂,并且有时候支持不运用的算法也是一个功能担负。
-
如何在运行时根据需求透明地更改目标的算法?将算法与目标本身解耦,然后防止上述问题?
事例
有这样一个事例,有一个出售单,需求核算汇率,可是不同国家的税法是不一样的,所以税费也不一样。所以咱们根据需求能够这样做:
计划一
enum RegionType {
case CN, US, DE, FR
}
class SalesOrder {
var taxType: RegionType
init(taxType: RegionType) {
self.taxType = taxType
}
func calculateTax() {
if (taxType == .CN) {
// CN***********
} else if (taxType == .US) {
// US***********
} else if (taxType == .DE) {
// DE***********
} else if (taxType == .FR) {
// FR***********
}
}
}
这种写法有什么不好呢?假如RegionType
添加一种,那么需求修正calculateTax
办法里的判别句子,这儿由于代码简略看着没有什么,可是假如代码复杂的话,那么基本上需求在所有用到RegionType
枚举值的当地都需求修正代码。
还有一个缺陷,一般发行一个APP在哪个国家是确认的,比如说这个APP是在我国发行的,那为什么要把其他国家的税法编译进来,不就是增大包体积了么?
所以咱们看改善后的代码
计划二
class Context {
// 表明一些上下文
}
protocol TaxStrategy {
func calculate(context: Context) -> Double
}
class CNTax: TaxStrategy {
func calculate(context: Context) -> Double {
// calculate CN tax
return 0
}
}
class USTax: TaxStrategy {
func calculate(context: Context) -> Double {
// calculate US tax
return 0
}
}
class DETax: TaxStrategy {
func calculate(context: Context) -> Double {
// calculate DE tax
return 0
}
}
class SalesOrder {
private var taxStrategy: TaxStrategy
init(taxStrategy: TaxStrategy) {
self.taxStrategy = taxStrategy
}
func calculateTax() {
func calculate() -> Double {
let context = Context()
return taxStrategy.calculate(context: context)
}
}
}
在计划二中,假如咱们想新增国家法国,那么咱们只要新增一个FRTax
类就行了:
class FRTax: TaxStrategy {
func calculate(context: Context) -> Double {
// calculate FR tax
return 0
}
}
这个设计形式经典的遵守了开闭原则,只对扩展开发,对修正关闭。
还有上面说的包体积的问题,咱们能够通过宏定义或许指定文件编译(不同类应该是要写在不同的文件里的),就能够在不同国家的APP里编译不同的代码逻辑了。
要点总结
-
Strategy及其子类为组件供给了一系列可重用的算法,然后能够使得类型在运行时方便地根据需求在各个算法之间进行切换。
-
Strategy形式供给了用条件判别句子以外的另一种挑选,消除条件判别句子,就是在解耦合。含有许多条件判别句子的代码通常都需求Strategy形式(并不绝对)。
-
假如Strategy目标没有实例变量,那么各个上下文能够同享同一个Strategy目标,然后节省目标开支。