写在文章前的话
我们在重构代码时,往往会用到设计模式,文章的案例主要是来自《Head First Design Patterns》或者是参考其他博主的案例,需要下载pdf文件的,可以点击这个链接:
链接: 《Head First Design Patterns》中文pdf
提取码: jh89
定义:将不同的策略(算法)进行封装,让他们之间可以相互的替换,此模式让测试的变化独立于试用策略的用户
策略模式如何运用,通过下面这个案例来讲解。
需求:模拟鸭子游戏,在游戏中会出现各种鸭子,一边游泳,一遍呱呱叫。
看到设个需求,我们一般直接的想法是,设计了一个鸭子基类Duck,包含鸭子的呱呱叫、游泳以及具体的展现方法,并让各种鸭子继承此基类,由于不同的鸭子会有不同的展现,所以会重写基类的display方法。
类“类图”图示如下:
上面的图是一个简化的类“类图”,Duck 是一个父类,其他类型的鸭子继承自 Duck。目前这么实现是没有问题的。
代码实现:
class Duck{
func swim(){
print("鸭子游泳喽~")
}
func quack(){
print("鸭子呱呱叫")
}
func display(){
}
}
class MallarDuck : Duck{
override func display() {
print("我是绿头鸭子")
}
}
class RedHeadDuck:Duck{
override func display() {
print("我是红头鸭子")
}
}
class RubberDuck:Duck{
override func display() {
print("橡皮鸭")
}
}
接下来,新需求来了:现在我们的让鸭子能飞
还是按着上面的思路,在基类增加一个 fly() 方法
但是,这样做导致了一个严重的后果,所有的鸭子都会继承 fly()方法
,如果我们的鸭子是橡皮鸭…橡皮鸭会飞…额…虽然看起来很有趣,但这是个不符合常理的现象,按着常理来说,橡皮鸭是不会飞的,所以我们这样实现是又问题的。
由于一些假的鸭子是不能飞的,所以要改变上面的实现方式,有两种解决办法
1、在基类中添加fly(),在不会飞的鸭子中重新fly,但是这样的话,会导致子类中存在些无用的方法
2、使用接口即协议,定义fly(),在需要实现 fly() 的鸭子子类中,实现接口即可,但是也会导致重复的产生
将飞的行为定义为一个协议,通过协议的扩展实现fly()方法,会飞的鸭子的子类新搜协议即可,不会飞的鸭子不用遵守该协议
具体代码实现:
protocol Flyable {
func fly()
}
extension Flyable{
func fly(){
print("我是会飞的鸭子,我用翅膀飞")
}
}
class Duck{
func swim(){
print("鸭子游泳喽~")
}
func quack(){
print("鸭子呱呱叫")
}
func display(){
}
}
//用绿头鸭实现会飞的功能
class MallarDuck : Duck, Flyable{
override func display() {
print("我是绿头鸭子")
}
}
class RedHeadDuck:Duck{
override func display() {
print("我是红头鸭子")
}
}
class RubberDuck:Duck{
override func display() {
print("橡皮鸭")
}
}
在设计模式中有不同的设计原则,策略模式主要参考一下3条原则
1、 找出程序中可能变化的地方,并且把它们独立出来,不要和不变的代码混在一起。
2、针对接口编程,而不是针对实现编程
3、多用组合,少用继承
根据这条设计原则,然后结合上述示例,我们使用“策略模式”来实现鸭子会飞的行为。
首先将飞行行为使用接口封装,然后将不同的飞行模式实现接口函数
//飞的行为协议
protocol Flyable {
func fly()
}
//使用翅膀飞的类
class FlyWithWings:Flyable{
func fly() {
print("我是会飞的鸭子,我用翅膀飞呀飞")
}
}
//什么都不会飞
class FlyNoWay:Flyable{
func fly() {
print("我是不会飞的鸭子")
}
}
其次,在Duck类中定义飞行为的委托代理者,并创建可以动态更改飞行行为的方法,并实现执行飞的行为的函数
class Duck{
//添加行为委托代理者
var flyBehavior : Flyable! = nil
func setFlyBehavior(_ flyBehavior : Flyable){
self.flyBehavior = flyBehavior
}
func swim(){
print("鸭子游泳喽~")
}
func quack(){
print("鸭子呱呱叫")
}
func display(){
}
//执行飞的行为
func performFly(){
guard self.flyBehavior != nil else {
return
}
self.flyBehavior.fly()
}
}
最后,鸭子子类中只需要在初始化时设置飞的类型即可
//用绿头鸭实现会飞的功能
class MallarDuck : Duck{
override init() {
super.init()
self.setFlyBehavior(FlyWithWings())
}
override func display() {
print("我是绿头鸭子")
}
}
class RedHeadDuck:Duck{
override init() {
super.init()
self.setFlyBehavior(FlyWithWings())
}
override func display() {
print("我是红头鸭子")
}
}
class RubberDuck:Duck{
override init() {
super.init()
self.setFlyBehavior(FlyNoWay())
}
override func display() {
print("橡皮鸭")
}
}
在使用了策略模式的基础上,对代码进行扩充时非常简单的。
我们只需要创建 模型鸭子 继承Duck 类,并在初始化中设置飞行类型即可
class ModelDuck : Duck{
override init() {
super.init()
self.setFlyBehavior(FlyWithWings())
}
override func display() {
print("鸭子模型")
}
}
创建一个发动机飞行的类,实现接口函数
class FlyAutomaticPower : Flyable{
func fly() {
print("我是用发动机飞的鸭子")
}
}
最后,测试是非常重要的,在使用设计模式重构的过程中,可以使用测试来检验功能是否正确
// print("鸭子:使用延展")
// let mallarDuck : MallarDuck = MallarDuck()
// mallarDuck.fly()
print("鸭子:使用接口")
var duck : Duck = MallarDuck()
duck.performFly()
duck.setFlyBehavior(FlyNoWay())
duck.performFly()
print("-----创建一个模型鸭子,且会飞")
duck = ModelDuck()
duck.performFly()
print("-----给模型鸭子装发动机,支持飞")
duck.setFlyBehavior(FlyAutomaticPower())
duck.performFly()
print("\n")