設計模式——適配器,橋接,外觀

前言

本文csdn地址:http://blog.csdn.net/game3108/article/details/51174787
本文主要以代碼形式實現每一種設計模式,算是自己的一種復習和實踐。相應的代碼,也會放到github上。
本文的類圖均來自于《Objective-C編程之道 iOS設計模式解析》。

本篇主要講:

  • 適配器
  • 橋接
  • 外觀

6.適配器

概念:將一個類的接口轉換成客戶希望的另一個接口。適配器模式使得原本由于接口不兼容而不能在一起工作的那些類可以一起工作。

適配器主要有兩種實現方式:

  • 類適配器
    通過繼承來適配兩個接口。
  • 對象適配器
    組合一個適配器對象的引用。

類圖:

Paste_Image.png
Paste_Image.png

何時使用:

  • 已有類的接口與需求不匹配。
  • 想要一個可復用的類,該類能夠同可能帶有不兼容接口的其他類協作。
  • 需要適配一個類的幾個不同自雷,可是讓每一個子類去子類化一個類適配器又不現實,那么可以使用對象適配器(也叫委托)來適配其父類的接口。

類適配器的例子:

EATarget對象

#import <Foundation/Foundation.h>

@protocol EATarget <NSObject>
- (void) request;
@end

EAAdaptee對象:

#import <Foundation/Foundation.h>

@interface EAAdaptee : NSObject
- (void) specificRequest;
@end

#import "EAAdaptee.h"

@implementation EAAdaptee
- (void) specificRequest{
}
@end

EAAdapter對象:

#import "EAAdaptee.h"
#import "EATarget.h"

@interface EAAdapter : EAAdaptee<EATarget>

@end

#import "EAAdapter.h"

@implementation EAAdapter

- (void) request{
    [super specificRequest];
    //to do something
    //xxxxxx
}

@end

對象適配器的例子:

delegate(委托)就是一個典型的對象適配器的用法。
UITableView,通過delegate回調didSelectRowAtIndexPath操作其他對象。其中UITableView為client,UITableViewDelegate為target

OASelectObject Adaptee:

#import <Foundation/Foundation.h>

@interface OASelectObject : NSObject
- (void) didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
@end

#import "OASelectObject.h"

@implementation OASelectObject
- (void) didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    
}
@end

OAUIViewController adapter對象

#import <UIKit/UIKit.h>

@interface OAUIViewController : UIViewController

@end

#import "OAUIViewController.h"
#import "OASelectObject.h"

@interface OAUIViewController ()<UITableViewDelegate,UITableViewDataSource>{
    OASelectObject *_selectObject;
}

@end

@implementation OAUIViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    
    _selectObject = [[OASelectObject alloc]init];
    
    UITableView *tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
    tableView.backgroundColor = [UIColor blackColor];
    tableView.delegate = self;
    tableView.dataSource = self;
    [self.view addSubview:tableView];
    // Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark UITableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    NSString *cellIdentifier = @"cell_identifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if ( !cell ){
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
        cell.backgroundColor = [UIColor grayColor];
    }
    
    return cell;
}

#pragma mark UITableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    [_selectObject didSelectRowAtIndexPath:indexPath];
}

@end

7.橋接

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

類圖:

Paste_Image.png

何時使用:

  • 不想在抽象與其實現之間形成固定的綁定關系(這樣就能在運行時切換實現)。
  • 抽象及其實現都應可以通過子類化獨立進行擴展。
  • 對抽象的實現進行修改不應影響客戶端代碼。
  • 如果每個實現需要額外的子類以細化抽象,則說明有必要把它們分為兩個部分。
  • 想在帶有不同抽象接口的多個對象之間共享一個實現。

代碼場景:
電視遙控器會根據不同電視機改變它的實現,而且遙控器本身也會改變。所以抽象出電視和遙控器兩個抽象類出來。

電視類:

#import <Foundation/Foundation.h>

@interface TV : NSObject
- (void)on;
- (void)off;
- (void)tuneChannel:(NSInteger)channel;
@end

#import "TV.h"

@interface TV(){
    NSInteger _nowChannel;
    BOOL _onShow;
}

@end

@implementation TV

- (void)on{
    _onShow = NO;
}
- (void)off{
    _onShow = YES;
}
- (void)tuneChannel:(NSInteger)channel{
    _nowChannel = channel;
}

@end

電視子類:

#import "TV.h"

@interface RCA : TV

@end

#import "RCA.h"

@implementation RCA
- (void)tuneChannel:(NSInteger)channel{
    NSLog(@"RCA tunechannel");
    [super tuneChannel:channel];
}
@end
#import "TV.h"

@interface Sony : TV

@end

#import "Sony.h"

@implementation Sony
- (void)tuneChannel:(NSInteger)channel{
    NSLog(@"Sony tunechannel");
    [super tuneChannel:channel];
}
@end

遙控類:

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

@interface RemoveControl : NSObject
@property (nonatomic, strong) TV *implementor;
- (void) on;
- (void) off;
- (void) setChannel:(NSInteger)channel;
@end

#import "RemoveControl.h"

@implementation RemoveControl
- (void) on{
    [_implementor on];
}
- (void) off{
    [_implementor off];
}
- (void) setChannel:(NSInteger)channel{
    [_implementor tuneChannel:channel];
}
@end

遙控子類:

#import "RemoveControl.h"

@interface ConcreteRemote : RemoveControl
- (void) setStation:(NSInteger)station;
- (void) nextChannel;
- (void) previousChannel;
@end

#import "ConcreteRemote.h"

@interface ConcreteRemote(){
    NSInteger _currentStation;
}

@end

@implementation ConcreteRemote
- (void) setStation:(NSInteger)station{
    _currentStation = station;
    [super setChannel:station];
}
- (void) nextChannel{
    [super setChannel:_currentStation + 1];
}
- (void) previousChannel{
    [super setChannel:_currentStation - 1];
}
@end

8.外觀

概念:為系統中的一組接口提供一個統一的接口。外觀定義一個高層接口,讓子系統更易于使用。

結構圖:

Paste_Image.png

何時使用:

  • 子系統正逐漸變得復雜。應用模式的過程中演化出許多類。可以使用外觀為這些子系統類提供一個較簡單的接口。
  • 可以使用外觀對子系統進行分層。每個子系統級別有一個外觀作為入口點。讓它們通過其外觀進行通信,可以簡化他們的依賴關系。

代碼場景:
家庭影院看電影的時候,可能要先放下屏幕,關燈,然后播放dvd等。使用外觀可以用一個家庭影院外觀類進行統一管理。

家庭外觀類:

#import <Foundation/Foundation.h>
#import "DvdPlayer.h"
#import "CdPlayer.h"
#import "Screen.h"
#import "TheaterLights.h"

@interface HomeTheaterFacade : NSObject
@property (nonatomic, strong) DvdPlayer *dvd;
@property (nonatomic, strong) CdPlayer *cd;
@property (nonatomic, strong) Screen *screen;
@property (nonatomic, strong) TheaterLights *lights;
- (void) watchMovie:(NSString*) movie;
- (void) endMovie;
@end

#import "HomeTheaterFacade.h"

@implementation HomeTheaterFacade

- (void) watchMovie:(NSString*) movie{
    NSLog(@"get ready to watch a movie");
    [_lights off];
    [_screen down];
    [_dvd on];
    [_dvd play:movie];
    
}
- (void) endMovie{
    NSLog(@"shutting movie theater down");
    [_lights on];
    [_screen up];
    [_dvd stop];
    [_dvd eject];
    [_dvd off];
}

@end

總結:

1.適配器,橋接,外觀的使用時機?

  • 適配器:用于設計完成之后,主要適用于解決兩個已有接口的匹配,而接口本身是一個黑匣子,你無法去修改接口的實現。
  • 橋接模式:用于設計的初期,參與接口的類是穩定的,用戶可以修改擴展和修改接口的類,但不能改變接口,通過接口繼承實現或者類繼承實現功能擴展。
  • 外觀模式:用于設計的初期,為一組子接口提供統一接口。相對于適配器的小粒度功能繼承,更像一個大粒度的適配器,當封裝系統演化的時候,需要新的外觀對象,而這個對象起到了適配器的作用。

參考資料

1.《Objective-C編程之道 iOS設計模式解析》
2.《Head First設計模式》
3.設計模式學習筆記十四:適配器模式、橋接模式與外觀模式

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容