設計模式系列 11-- 橋接模式

西木柚子發表於2016-12-19

設計模式系列 11-- 橋接模式
image

假設要實現一個給客戶傳送提示訊息的功能,傳送的訊息型別可分為:普通訊息、加急訊息、特加急訊息等等,而每種訊息的傳送的方式一般有:系統內推送、手機簡訊、電子郵件等等。如果讓我們來實現,會怎麼做呢?

我們先來實現一個簡單的版本,使用系統推送和電子郵件傳送普通訊息,實現起來不叫簡單,就不展示程式碼了,直接看UML結構圖

設計模式系列 11-- 橋接模式
image

很簡單的實現對吧,現在再增加一個加急訊息的傳送,也是通過系統推送和Email兩種方式傳送,而且加急訊息還額外多了一個方法,想了想簡單嘛,直接擴充套件現有的介面不就可以了嗎,此時UML結構如如下:

設計模式系列 11-- 橋接模式
image

如果在增加一個特加急訊息的傳送呢,也是兩種方式,也有一個額外的方法,繼續擴充套件嘛,此時UML機構如如下:

設計模式系列 11-- 橋接模式
image

是不是感覺類的數目劇增了,還沒完呢。現在需要給每種訊息型別增加一種傳送方式:手機傳送。繼續改,UML結構圖如下:

設計模式系列 11-- 橋接模式
image

此時類的數目已經非常多了,如果繼續增加訊息型別或者傳送方式,那麼又要重複擴充套件,類的數目會急劇增加。

我們來仔細分析下上面的實現,其實這裡面有兩個變化的維度:訊息型別和傳送方式。他們之間是交織在一起的,如下圖所示:

設計模式系列 11-- 橋接模式
image

由於這兩個維度是交織在一起,那麼他們之間的組合方式就有9種,也就是我們上面看到的有9個類,如果此時任何一個維度發生變化,都會導致與之關聯的另一個維度也需要修改。而且一個維度的上的數目的增加,都會導致整體類的數目成倍數的增加。如果訊息的傳送型別和傳送方式都有10種的話,那麼就需要實現100個類,想想就恐怖。

那麼自然而然的我們就想到把這兩個維度分開,讓他們獨自變化,互不影響,需要用的是會把他們組合在一起就行了。這樣一個維度的類增加,不會導致另一個維度類也需要跟著一起增加。

再次證明:多用組合少用繼承

那麼如何實現呢?這就要請出我們今天的主角:橋接模式。


定義

將抽象部分與它的實現部分分離,使它們都可以獨立地變化。

抽象部分和實現部分就是兩個不同的維度,抽象部分對應上面的訊息型別,實現部分對應上面的訊息傳送方式。現在我們獨立實現兩個部分,然後訊息型別部分想呼叫訊息實現部分去傳送訊息該怎麼辦呢?很簡單嘛,讓抽象部分持有實現部分的介面,面向介面程式設計就可以了,這就是橋接模式名字的由來。橋接抽象部分和實現部分,下面看UML結構圖會更加的清晰。


UML結構如及說明

設計模式系列 11-- 橋接模式
image

可以看到抽象部分的抽象類和實現部分的介面是聚合關係,表示抽象部分持有實現部門的介面,這樣抽象部分就可以呼叫實現部分完成功能了。

分析到這裡,大家應該對橋接模式有一個大致的瞭解了吧,下面就來看看如何使用橋接模式來實現上面的訊息傳送功能。


程式碼實現

1、訊息型別抽象類

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

@interface abstractMessage : NSObject
@property(strong,nonatomic)id<messageImplement> messageIm;

-(void)send:(NSMutableString*)message;
- (instancetype)initWithImplement:(id<messageImplement>)implement;
@end

====================
#import "abstractMessage.h"

@implementation abstractMessage

- (instancetype)initWithImplement:(id<messageImplement>)implement
{
    self = [super init];
    if (self) {
        self.messageIm = implement;
    }
    return self;
}

-(void)send:(NSMutableString*)message{

}

@end複製程式碼

2、具體訊息型別

下面只展示了普通訊息的具體類實現,其他兩種方式類似,詳細見demo

#import "abstractMessage.h"

@interface commonMessage : abstractMessage

@end


====================

#import "commonMessage.h"

@implementation commonMessage

-(void)send:(NSMutableString *)message{
    [message insertString:@"【普通訊息:" atIndex:0];
    [message appendString:@"】"];
    [self.messageIm sendMessage:message];
}
@end複製程式碼

3、訊息傳送介面

#import <Foundation/Foundation.h>

@protocol messageImplement <NSObject>

-(void)sendMessage:(NSString *)message;

@end複製程式碼

4、具體的訊息傳送方式

下面只展示使用系統內推送方式傳送訊息的方式,其他兩種訊息傳送方式類似,不在展示,具體見demo

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

@interface messageSMS : NSObject<messageImplement>

@end


=================

#import "messageSMS.h"

@implementation messageSMS

-(void)sendMessage:(NSString *)message{
    NSLog(@"使用系統內訊息方式傳送訊息,訊息內容:%@", message);
}
@end複製程式碼

5、測試

 id<messageImplement> messageIMP = [messageMobile new];
 abstractMessage *message = [[specialUrgencyMessage alloc]initWithImplement:messageIMP];
 NSMutableString *mStr = [[NSMutableString alloc]initWithString:@"大海啊,全是水,駿馬啊,四條腿"];
 [message send:mStr];複製程式碼

6、輸出

2016-12-15 16:56:43.356 橋接模式[66573:2541266] 使用手機方式傳送訊息,訊息內容:【特別加急訊息:大海啊,全是水,駿馬啊,四條腿】複製程式碼

你可以任意組合訊息型別和訊息傳送方式,此時類的數目只有6個,比之前的繼承實現方式9個少了。如果兩者都有10種實現方式,那麼使用繼承方式就需要100個類,而使用橋接模式只要20個類,看到了橋接模式的巨大優點吧。


優缺點

設計模式系列 11-- 橋接模式
image


思考

橋接模式,需要理解橋接二字的由來,看上面的UML圖就明白了,是在抽象和實現之間橋接。因為他們現在分開了,但是抽象部分必須使用實現部分去實現功能,所以抽象部分必須引用實現部分,這就是橋接。

橋接模式對比繼承的有點是把本來混在一起的兩個變化未讀分開,讓他們獨立變化,這樣互相不影響,減少了類的數目,也方便擴充套件,而且可以動態替換功能。比如上面的訊息傳送功能,同樣是傳送普通訊息,我可以選擇手機、email其中的任何一種方式,只需要組合起來就行了,比繼承更加靈活。

擴充套件下去,我們上面只是兩個維度在變化,那麼如果是三個、四個維度在變化呢?如果你使用繼承去實現,那就完了,不知道要重複寫多少程式碼,而使用橋接模式,就可以把這些變化維度全部獨立分開實現,然後客戶端想怎麼組合就怎麼組合。


Demo下載

橋接模式Demo

相關文章