iOS 設(shè)計模式系列一:策略模式

什么是策略模式

策略模式定義了算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化獨(dú)立于使用算法的客戶。——《Head First 設(shè)計模式》

這里引用了《Head First 設(shè)計模式》里的定義。其中算法族是指一系列算法,可以把算法理解為對象的行為(方法)。

這里用到了一個設(shè)計原則:

找出應(yīng)用中可能需要變化之處,把它們獨(dú)立出來,不要和那些不需要變化的代碼混在一起。

這是一個讓我耳目一新的思想,我的固有思維里總是把不變的東西封裝起來,比如基類的使用(當(dāng)然,這些變化里其實是有不變的——相同方法的不同實現(xiàn))。而這種思想是“把會變化的部分取出來并封裝起來,以便以后可以輕易地改動或擴(kuò)充此部分,而不影響不需要變化的其它部分”。在接下來的實踐中我們會更加深刻地體會這種思想。

需求場景

現(xiàn)有一個野鴨類MallardDuck,野鴨會飛 fly(),會叫 quack()。很快有了新的需求,我們要實現(xiàn)一個家鴨(飼養(yǎng)的鴨子)的類DomesticDuck,家鴨不會飛fly(),叫聲quack()(嘎嘎叫)也和野鴨MallardDuck(咕咕叫)不同。

通常情況下利用面向?qū)ο蟮乃枷耄鶕?jù)鴨子的共性我們會抽出一個鴨子基類Duck。然后繼承創(chuàng)建野鴨類MallardDuck和家鴨類DomesticDuck,并在子類里分別重寫飛的方法fly()和叫的方法quack()


但是,這樣實現(xiàn)方式有一個問題,我們知道需求總是在不斷變化的,產(chǎn)品經(jīng)理某一天突發(fā)奇想要很多其它類別的鴨子,并且鴨子的飛行的方式fly()和叫的方式quack()可能一樣,也可能不一樣。如果我們總是在子類里重寫方法,就沒辦法對相同的方法進(jìn)行重用,而且也不能方便地對每個類的方法進(jìn)行修改。

所以,我們應(yīng)該把鴨子的行為從鴨子類Duck里剝離出來單獨(dú)封裝。

解決方案

這里有兩種解決方案。

實現(xiàn)繼承

  • 方案一:將行為封裝為類,通過繼承在子類重寫行為方法,我們將之稱為實現(xiàn)繼承。
  • 具體實現(xiàn):創(chuàng)建FlyBehavior類和QuackBehavior類,分別在類中實現(xiàn)fly()quack(),然后繼承創(chuàng)建子類FlyWithWingsFlyNoWay。對QuackBehavior做同樣的處理,GuGuQuackGaGaQuack


    這樣似乎也能解決問題,但是我們根據(jù)針對接口編程,而不是針對實現(xiàn)編程的設(shè)計原則采用第二種解決方案。“針對接口編程”的意思是“針對超類型編程”,即“變量的聲明類型應(yīng)該是超類型,通常是一個抽象類或者是一個接口”。利用多態(tài)機(jī)制,只要是實現(xiàn)了該接口的對象都可以指定給這個變量,運(yùn)行時根據(jù)實際賦予的變量執(zhí)行真正的行為。實現(xiàn)和接口分離,而不是死綁在一起。

接口繼承

  • 方案二:把行為設(shè)計成接口,再讓具體的行為類去實現(xiàn)對應(yīng)的方法。
  • 具體實現(xiàn):在 Objective-C 中,我們可以用協(xié)議Protocal來定義飛行為<FlyBehavior>和叫行為<QuackBehavior>,讓具體的行為類(FlyWithWings<FlyBehavior>FlyNoWay<FlyBehavior>GuGuQuack<QuackBehavior>GaGaQuack<QuackBehavior>)去實現(xiàn)相應(yīng)的方法。

代碼實現(xiàn)

下面給出方案二的代碼實現(xiàn)。

Duck 類

Duck.h 文件

#import <Foundation/Foundation.h>
#import "FlyBehavior.h"
#import "QuackBehavior.h"

@interface Duck : NSObject

@property (strong, nonatomic)id<FlyBehavior> flyBehavior;
@property (strong, nonatomic)id<QuackBehavior> quackBehavior;

- (void)performFly;
- (void)performQuack;

@end

Duck.m 文件

#import "Duck.h"

@implementation Duck

- (void)performFly{
    [self.flyBehavior fly];
}

- (void)performQuack{
    [self.quackBehavior quack];
}

@end

MallardDuck 類

MallardDuck.h 文件

#import "Duck.h"

@interface MallardDuck : Duck

@end

MallardDuck.m 文件

#import "MallardDuck.h"
#import "FlyWithWings.h"
#import "GuGuQuack.h"

@implementation MallardDuck

- (instancetype)init{
    if (self = [super init]) {
        self.flyBehavior = [[FlyWithWings alloc]init];
        self.quackBehavior = [[GuGuQuack alloc]init];
        NSLog(@"I'm a mallardDuck");
    }
    return self;
}

@end

DomesticDuck 類

DomesticDuck.h 文件

#import "Duck.h"

@interface DomesticDuck : Duck

@end

DomesticDuck.m 文件

#import "DomesticDuck.h"
#import "FlyNoWay.h"
#import "GaGaQuack.h"

@implementation DomesticDuck

- (instancetype)init{
    if (self = [super init]) {
        self.quackBehavior = [[GaGaQuack alloc]init];
        self.flyBehavior = [[FlyNoWay alloc]init];
        NSLog(@"I'm a DomesticDuck");
    }
    return self;
}

@end

FlyBehavior 協(xié)議

FlyBehavior.h 文件

#import <Foundation/Foundation.h>

@protocol FlyBehavior <NSObject>

@required

- (void)fly;

@end

FlyNoWay 類

FlyNoWay.h 文件

#import <Foundation/Foundation.h>
#import "FlyBehavior.h"

@interface FlyNoWay : NSObject <FlyBehavior>

- (void)fly;

@end

FlyNoWay.m 文件

#import "FlyNoWay.h"

@implementation FlyNoWay

- (void)fly{
    NSLog(@"I can't fly");
}

@end

FlyWithWings 類

FlyWithWings.h 文件

#import <Foundation/Foundation.h>
#import "FlyBehavior.h"

@interface FlyWithWings : NSObject <FlyBehavior>

- (void)fly;

@end

FlyWithWings.m 文件

#import "FlyWithWings.h"

@implementation FlyWithWings

- (void)fly{
    NSLog(@"I can fly");
}

@end

整合調(diào)用

main.m 文件

#import <Foundation/Foundation.h>
#import "MallardDuck.h"
#import "DomesticDuck.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
      
        Duck *mallardDuck = [[MallardDuck alloc]init];
        [mallardDuck performFly];
        [mallardDuck performQuack];
        
        Duck *domesticDuck = [[DomesticDuck alloc]init];
        [domesticDuck performFly];
        [domesticDuck performQuack];
    }
    return 0;
}

源代碼地址

Github地址: https://github.com/Zentopia/DesignPatterns

參考資料

  • 《Head First 設(shè)計模式(Java)》
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容