前言
比方说有一家电视机制造商,他们生产的每台电视机都带有一个遥控器,用户可以用遥控器进行切换频道之类的操作。这里遥控器就是电视机的接口。如果每个电视机型号需要一个专用的遥控器,那么单是遥控器就会导致设计激增。我们可以把遥控器逻辑同实际的电视机型号分离开来,这样电视机型号的改变就不会对遥控器的设计有任何影响。遥控器的同一个设计可以被复用和扩展,而不会影响其他电视机型号。 在面向对象的软件设计中也有类似的情形,从针对不同的实现中,分离出其可被复用的抽象称为桥接模式。
什么是桥接模式
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种模式涉及到一个作为桥接的接口,使得抽象层次结构从其实现中分离出来,抽象层定义了供客户端使用的上层的抽象接口,实现层次则定义了供抽象层次使用的底层接口,实现类的引用被封装与抽象层的实例中时,桥接就形成了。这两种类型的类可被结构化改变而互不影响。
桥接模式的类图.png Abstraction
是定义了供客户端使用的上层抽象接口的父接口。它有一个对 Implementor
实例的引用,Implementor
定义了实现类的接口。这个接口不必跟 Abstraction
的接口一致。Implementor
的接口提供基本操作,而 Abstraction
的上层操作基于这些基本操作。当客户端向 Abstraction
的实例发送 operation
消息时,这个方法向 imp
发送 operationImp
消息。实际的 ConcreteImplementator
将作出响应并接收任务。
什么时候使用桥接模式
- 不想在抽象与其实现之间形成固定的绑定关系
- 抽象及其实现都应可以通过子类独立化进行扩展
- 对抽象的实现进行修改不应影响客户端代码
- 如果每个实现需要额外的子类以细化抽象,则说明有必要把它们分成两个部分
- 想在带有不同抽象接口的多个对象之前共享一个实现。
实现系统可能有多个角度分类,每一种角度都可能变化。在有多种可能会变化的情况下,用继承会扩展起来不灵活。
桥接模式的优缺点
桥接模式的优点
- 抽象和实现的分离。
- 优秀的扩展能力。
- 实现细节对客户透明。
桥接模式的缺点
桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
桥接模式的实现
以虚拟操作手柄为例,虚拟控制器接收了用户的输入动作,涉及上、下、左、右、选中、开始、按键 A 和 B,而仿真器则负责对命令进行处理底层事件,两个层次结构都有不同的接口,但是通过对象组合的关系,在两个层次接口的上层之前形成桥接,从而联系起来。
定义用户输入动作命令类型
typedef enum
{
kConsoleCommandUp,
kConsoleCommandDown,
kConsoleCommandLeft,
kConsoleCommandRight,
kConsoleCommandSelect,
kConsoleCommandStart,
kConsoleCommandAction1,
kConsoleCommandAction2
} ConsoleCommand;
定义一个仿真器 <ConsoleEmulator>
协议,处理底层指令,并定义实体类遵守 <ConsoleEmulator>
协议。
@protocol ConsoleEmulator <NSObject>
- (void) loadInstructionsForCommand:(ConsoleCommand) command;
- (void) executeInstructions
@end
@interface GameBoyEmulator : NSObject <ConsoleEmulator>
{
}
// overridden behaviors from the abstract class
- (void) loadInstructionsForCommand:(ConsoleCommand) command;
- (void) executeInstructions;
// other behaviors and properties.
@end
@implementation GameBoyEmulator
- (void) loadInstructionsForCommand:(ConsoleCommand) command
{
NSLog(@"load specific instructions for the Game Boy");
//
}
- (void) executeInstructions
{
NSLog(@"execute loaded instructions ");
// execute loaded instructions
}
@end
@interface GameGearEmulator : NSObject <ConsoleEmulator>
{
}
// overridden behaviors from the abstract class
- (void) loadInstructionsForCommand:(ConsoleCommand) command;
- (void) executeInstructions;
// other behaviors and properties.
@end
@implementation GameGearEmulator
- (void) loadInstructionsForCommand:(ConsoleCommand) command
{
// load specific instructions for
// the Game Gear
NSLog(@"load specific instructions for the Game Gear");
}
- (void) executeInstructions
{
// execute loaded instructions
NSLog(@"execute loaded instructions ");
}
@end
定义一个 ConsoleController
控制器抽象类,其与遵守 ConsoleEmulator
协议的实体类建立联系。
@interface ConsoleController : NSObject
@property (nonatomic, strong) id <ConsoleEmulator> emulator;
- (void) setCommand:(ConsoleCommand) command;
@end
@implementation ConsoleController
- (void) setCommand:(ConsoleCommand) command
{
[_emulator loadInstructionsForCommand:command];
[_emulator_ executeInstructions];
}
@end
定义 TouchConsoleController
继承 ConsoleController
。实现接收用户的动作,并调用父类的 - (void) setCommand:(ConsoleCommand) command;
方法。
@interface TouchConsoleController : ConsoleController
{
}
- (void) up;
- (void) down;
- (void) left;
- (void) right;
- (void) select;
- (void) start;
- (void) actionA;
- (void) actionB;
@end
@implementation TouchConsoleController
- (void) up
{
[super setCommand:kConsoleCommandUp];
}
- (void) down
{
[super setCommand:kConsoleCommandDown];
}
- (void) left
{
[super setCommand:kConsoleCommandLeft];
}
- (void) right
{
[super setCommand:kConsoleCommandRight];
}
- (void) select
{
[super setCommand:kConsoleCommandSelect];
}
- (void) start
{
[super setCommand:kConsoleCommandStart];
}
- (void) actionA
{
[super setCommand:kConsoleCommandAction1];
}
- (void) actionB
{
[super setCommand:kConsoleCommandAction2];
}
@end
总结
当程序逐渐变大变复杂时,会有越来越多小型的类从设计和应用模式中演化出来。如果没有一种简化的方式来使用这些类,客户端代码最终将变得越来越大、越来越难以理解,而且,维护会繁琐无趣。外观有助于提供一种更为简洁的方式来使用子系统中的这些类,处理这些子系统类的默认行为的,可能只是定义在外观中的一个简单的方法,而不必直接去使用这些类。
网友评论