前言
本文csdn地址:http://blog.csdn.net/game3108/article/details/51174787
本文主要以代码形式实现每一种设计模式,算是自己的一种复习和实践。相应的代码,也会放到github上。
本文的类图均来自于《Objective-C编程之道 iOS设计模式解析》。
本篇主要讲:
- 适配器
- 桥接
- 外观
6.适配器
概念:将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能在一起工作的那些类可以一起工作。
适配器主要有两种实现方式:
- 类适配器
通过继承来适配两个接口。 - 对象适配器
组合一个适配器对象的引用。
类图:
何时使用:
- 已有类的接口与需求不匹配。
- 想要一个可复用的类,该类能够同可能带有不兼容接口的其他类协作。
- 需要适配一个类的几个不同自雷,可是让每一个子类去子类化一个类适配器又不现实,那么可以使用对象适配器(也叫委托)来适配其父类的接口。
类适配器的例子:
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.桥接
概念:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
类图:
何时使用:
- 不想在抽象与其实现之间形成固定的绑定关系(这样就能在运行时切换实现)。
- 抽象及其实现都应可以通过子类化独立进行扩展。
- 对抽象的实现进行修改不应影响客户端代码。
- 如果每个实现需要额外的子类以细化抽象,则说明有必要把它们分为两个部分。
- 想在带有不同抽象接口的多个对象之间共享一个实现。
代码场景:
电视遥控器会根据不同电视机改变它的实现,而且遥控器本身也会改变。所以抽象出电视和遥控器两个抽象类出来。
电视类:
#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.外观
概念:为系统中的一组接口提供一个统一的接口。外观定义一个高层接口,让子系统更易于使用。
结构图:
何时使用:
- 子系统正逐渐变得复杂。应用模式的过程中演化出许多类。可以使用外观为这些子系统类提供一个较简单的接口。
- 可以使用外观对子系统进行分层。每个子系统级别有一个外观作为入口点。让它们通过其外观进行通信,可以简化他们的依赖关系。
代码场景:
家庭影院看电影的时候,可能要先放下屏幕,关灯,然后播放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.设计模式学习笔记十四:适配器模式、桥接模式与外观模式