已有的类与新的接口之间不兼容的问题相当普遍,人们已为它找到了一个解决方案。这个解决方案就是适配器。
1 何为适配器 what
适配器的主要作用是把被适配者的行为传递给管道另一端的客户端。
将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类一起工作。
实现适配器有两种方式:
1)类适配器:通过继承来适配两个接口。继承Adaptee,遵从<Target>协议。
2)对象适配器:通过组合来适配。遵从<Target>协议,组合了一个队Adaptee的引用。
区别:
类适配器
只针对单一的具体Adaptee类,把Adaptee适配到Target;
易于重载Adaptee的行为,因为是通过直接的子类化进行的适配;
只有一个Adapter对象,无需额外的指针间接访问Adaptee。
对象适配器
可以适配多个Adaptee及子类;
难以重载Adaptee的行为,需要借助于子类的对象而不是Adaptee本身;
需要额外的指针以间接访问Adaptee并适配器行为。
适配器模式中的角色
1) 目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,额可以是接口。
2)需要适配的类(Adaptee):需要适配的类或是陪着类。
3)适配器(Adaper):通过包装一个需要适配的对象,把原接口转换成目标接口。
2 何时使用适配器模式
1) 系统需要使用现有的类,而这些类的接口不符合系统的接口。
2) 想要建立一个可以重用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
3) 两个类所做的事情相同或相似,但是具有不同接口的时候。
4) 旧的系统开发的类已经实现了一些功能,但是客户端却只能以另外接口的形式访问,但我们不希望手动更改原有类的时候。
5) 使用第三方组件,组件接口定义和自己定义的不同,不希望修改自己的接口,但是要使用第三方组件接口的功能。
3 委托模式主要是适配器模式,也可以实现某些其他设计模式的意图,比如装饰模式。
4 objective-c 协议实现适配器模式
如果要复用某个东西,从面向对象编程的角度来说,就要把那个东西放到对象中。(命令对象NSInvocation,可被放入队列并复用,可以对封装的操作进行撤销和恢复)
线色变更机制的设计与实现:
SetStrokeColorCommand是客户端是Command的子类
Command:NSObject
方法:- (void) execute;
1)<SetStrokeColorCommandDelegate>
- (void) command:(SetStrokeColorCommand *)command didRequestColorComponentsForRed:(CGFloat *)red green:(CGFloat *) blue:(CGFloat *)blue;
//通过传递红绿蓝引用参数返回队里的RGB值
- (void) command:(SetStrokeColorCommand *)command didFinishColorUpdateWithColor:(UIColor *)color;
//当颜色更新过程结束时,此方法将会被调用。命令对象会把自己和更新后的颜色对象传给适配器。
2)实现客户端 SetStrokeColorCommand
SetStrokeColorCommand:Command
实例:id <SetStrokeColorCommandDelegate> delegate
方法:重写 - (void)execute;
- (void)execute
{
CGFloat redValue = 0.0f;
CGFloat greenValue = 0.0f;
CGFloat blueValue = 0.0f;
[delegate command:self didRequestColorComponetsForRed:&redValue green:&greenValue blue:&blueValue];
UIColor *color = [UIColor colorWithRed:redValue green:greenValue blue:blueValue alpha:1.0];
//把它复制给当前CanvasViewController
CoordinatingController *coordinator = [CoordinatingController sharedInstance];
CanvasViewController *controller = [coordinator canvasViewController];
[controller setStrokeColor:color];
//转发更新成功消息
[delegate command:self didFinishColorUpdateWithColor:color];
}
3) 创建被适配者
PaletteViewController
#pragma mark SetStrokeColorCommandDelgete methods
- (void) command:(SetStrokeColorCommand *)command didRequestColorComponentsForRed:(CGFloat *)red green:(CGFloat *) blue:(CGFloat *)blue
{
*red = [redSlider value];
*green = [greenSlider value];
*blue = [blueSlider value];
}
- (void) command:(SetStrokeColorCommand *)command didFinishColorUpdateWithColor:(UIColor *)color
{
[paletteView setBackgroundColor:color];
}
#pragma mark Slider event handler
- (IBAction)onCommandSliderValueChanged:(CommandSlider *)slider
{
[[slider command] excute];
}
//定义客户端期待的接口
Target
方法:Request NSLog(@"this is a common request");
//定义需要适配的类
Adaptee
方法:SepecificRequst NSLog(@"This is a special request");
//定义适配器
Adapter:Target
方法:重写Request 实际调用SepecificRequst
//客户端代码,对于客户端来说,调用的就是Target的Request
Target target = new Adapter();
[target Request];
5 使用块实现适配器模式
关于块:块实际上是OC对象。块被看做栈中的匿名对象,离开了作用域就会被销毁。块对retain没有实际作用。要想retain块,需要把它移动到堆中。块字面量可以与消息调用定义在一起,但是C函数指针缺无法做到这一点。
跟涉及更为正式的协议与类相比,这种方式使得代码和结构更加简洁,适配器更加直接而随意,却又保持了适配器模式的原汁原味。