深入解析iOS应用开发中对设计模式中的桥接模式的使用

引言
  在项目开发中,我们会遇到这样的一种场景:某些类型由于自身的逻辑,往往具有两个或多个维度的变化,比如说大话设计模式书中所说的手机,它有两个变化的维度:一是手机的品牌,可能有三星、苹果等;二是手机上的软件,可能有QQ、微信等。如何应对这种“多维度的变化”?怎样利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度?这就是本章桥接模式所要解决的问题。

何为桥接模式?

桥接模式的目的是把抽象层次结构从其实现中分离出来,使其能够独立变更。抽象层定义了供客户端使用的上层的抽象接口。实现层定义了供抽象层使用的底层接口。实现类的引用被封装于抽象层的实例中,桥接就形成。(与外观模式有一定的相似之处)。

桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。

桥接模式的实例应用

比如有一家电视机制造商,他们生产的每台电视都带一个遥控器,用户可以用遥控器进行频道切换之类的操作。在这里遥控器是控制电视机的接口,如果每个电视机型号需要一个专用的遥控器,那么单是遥控器就会导致设计激增。不过,每个遥控器都有些功能是各种型号电视机共有的,比如切换频道、调节音量和电源开关。而且每台电视机都应该能够通过基本命令接口,响应遥控器发来的这些命令。我们可以把遥控器逻辑同实际的电视机型号分离开来。这样电视机型号的改变就不会对遥控器的设计有任何的影响。遥控器的同一个设计可以被复用和扩展,而不会影响其他电视机型号。如下图所示:

AbstractRemoteControl是定义了供客户端使用的上层接口的父接口。它有一个对TVProtocol视力的引用,TVProtocol定义了实现类的接口。这个接口不必跟AbstractRemoteControl的接口一致,其实两个接口可以完全不同。TVProtocol的接口提供基本的操作,而AbstractRemoteControl的上层操作基于这些基本操作。当客户端向AbstractRemoteControl的实例发送operation消息时,这个方法向imp发送operationImp消息,底下的实际由TVA或TVB将作出响应并接受任务。

因此想要往系统中添加新的TVProtocol时,所要做的只是为TVProtocol创建一个新的实现类,响应operationImp消息并在其中执行任何具体的操作。不过,这对AbstractRemoteControl方面不会有任何影响。同样,如果想修改AbstractRemoteControl的接口或者创建更细化的AbstractRemoteControl类,也不会影响桥接的另一头。

来看下具体的代码实现,先看下抽象部分的代码实现,AbstractRemoteControl代码如下:

代码如下:

#import <Foundation/Foundation.h>
#import "TVProtocol.h"
@interface AbstractRemoteControl : NSObject
 
@property (nonatomic, weak) id<TVProtocol> tvProtocol;
 
- (void)detectTVFunction;
 
@end

代码如下:

#import "AbstractRemoteControl.h"
 
@implementation AbstractRemoteControl
 
- (void)detectTVFunction {
    NSLog(@"检测电视机具备的功能,由子类来进行实现");
}
 
@end

在AbstractRemoteControl类中保持了对TVProtocol实例对象的引用,定义了供客户端使用的上层抽象接口detectTVFunction,而这个方法的具体实现则由其子类去实现,ConcreteRemoteControl代码如下:

代码如下:

#import "AbstractRemoteControl.h"
 
@interface ConcreteRemoteControl : AbstractRemoteControl
 
// 重写该方法
- (void)detectTVFunction;
 
@end

代码如下:

#import "ConcreteRemoteControl.h"
 
@implementation ConcreteRemoteControl
 
- (void)detectTVFunction {
    [self.tvProtocol switchChannel];
    [self.tvProtocol adjustVolume];
    [self.tvProtocol powerSwitch];
}
 
@end

从这里我们可以看出,当客户端向ConcreteRemoteControl的实例发送detectTVFunction消息时,这个方法向TVProtocol发送switchChannel、adjustVolume、powerSwitch三个消息,TVA或TVB将作出响应并接受任务。至此,抽象部分代码已经完成了,接着看下实现部分的代码,TVProtocol代码如下:

代码如下:

#import <Foundation/Foundation.h>
 
@protocol TVProtocol <NSObject>
 
@required
 
- (void)switchChannel; // 切换频道
 
- (void)adjustVolume;  // 调节音量
 
- (void)powerSwitch;   // 电源开关
 
@end
    这就是一个协议,协议里面定义了三个方法,以后在创建电视机实例的时候,就必须遵守该协议,从而保证了电视机具有相同的功能。AbstractTV的代码如下:

#import <Foundation/Foundation.h>
#import "TVProtocol.h"
@interface AbstractTV : NSObject <TVProtocol>
 
@end

代码如下:

#import "AbstractTV.h"
 
@implementation AbstractTV
 
- (void)switchChannel {
    NSLog(@"切换频道,由具体的子类来实现");
}
 
- (void)adjustVolume {
    NSLog(@"调节音量,由具体的子类来实现");
}
 
- (void)powerSwitch {
    NSLog(@"电源开关,由具体的子类来实现");
}
 
@end

TVA的代码如下:

代码如下:

#import "AbstractTV.h"
 
@interface TVA : AbstractTV
 
// 重写这三个方法
- (void)switchChannel;
- (void)adjustVolume;
- (void)powerSwitch;
 
@end

代码如下:

#import "TVA.h"
 
@implementation TVA
 
- (void)switchChannel {
    NSLog(@"电视机A 具备了切换频道的功能");
}
 
- (void)adjustVolume {
    NSLog(@"电视机A 具备了调节音量的功能");
}
 
- (void)powerSwitch {
    NSLog(@"电视机A 具备了电源开关的功能");
}
 
@end

TVB的代码如下:

代码如下:

#import "AbstractTV.h"
 
@interface TVB : AbstractTV
 
// 重写这三个方法
- (void)switchChannel;
- (void)adjustVolume;
- (void)powerSwitch;
 
@end

代码如下:

#import "TVB.h"
 
@implementation TVB
 
- (void)switchChannel {
    NSLog(@"电视机B 具备了切换频道的功能");
}
 
- (void)adjustVolume {
    NSLog(@"电视机B 具备了调节音量的功能");
}
 
- (void)powerSwitch {
    NSLog(@"电视机B 具备了电源开关的功能");
}
 
@end

到这里,桥接模式代码已经完成了,在客户端该怎么去应用呢?我们通过下面的客户端代码来说明,如下:

代码如下:

#import "ViewController.h"
#import "AbstractRemoteControl.h"
#import "ConcreteRemoteControl.h"
#import "TVProtocol.h"
#import "AbstractTV.h"
#import "TVA.h"
#import "TVB.h"
 
typedef id<TVProtocol> TVProtocol; //在这里要进行一下转换声明,否则类中不能识别TVProtocol.
 
@interface ViewController ()
 
@end

代码如下:

@implementation ViewController
 
- (void)viewDidLoad {
    [super viewDidLoad];
 
    AbstractRemoteControl *remoteControl = [[ConcreteRemoteControl alloc] init];
    TVProtocol tvProtocol = [[TVA alloc] init];
    remoteControl.tvProtocol = tvProtocol;
    
    [remoteControl detectTVFunction];
    
    NSLog(@"///////////////////////////////");
    
    tvProtocol = [[TVB alloc] init];
    remoteControl.tvProtocol = tvProtocol;
    [remoteControl detectTVFunction];
    
    /**
     *  桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
     *  在本例中,AbstractRemoteControl是抽象部分,TVProtocol是其实现部分。
     */
    
}
 
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
 
@end

日志输出如下:

2015-09-01 22:59:06.295 Bridge[16464:703747] 电视机A 具备了切换频道的功能
2015-09-01 22:59:06.295 Bridge[16464:703747] 电视机A 具备了调节音量的功能
2015-09-01 22:59:06.296 Bridge[16464:703747] 电视机A 具备了电源开关的功能
2015-09-01 22:59:06.296 Bridge[16464:703747] ///////////////////////////////
2015-09-01 22:59:06.296 Bridge[16464:703747] 电视机B 具备了切换频道的功能
2015-09-01 22:59:06.296 Bridge[16464:703747] 电视机B 具备了调节音量的功能
2015-09-01 22:59:06.296 Bridge[16464:703747] 电视机B 具备了电源开关的功能

通过桥接模式的应用,我们可以把抽象部分与实现部分分离,使它们都可以独立的变化。比如在本例中,对AbstractRemoteControl的修改,不会影响到TVProtocol。同样对TVProtocol的修改,也不会影响AbstractRemoteControl。这正是桥接模式带给我们的便利性。
   
小结

总的来说,桥接模式的本质在于“分离抽象和实现”。

桥接模式的优点:

桥接模式使用聚合关系,解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。
提高了系统的可扩展性,可以独立地对抽象部分和实现部分进行扩展。
可减少子类的个数,这个在前面讲手机示例的时候进行分析了。

桥接模式的缺点:
桥接模式的引入会增加系统的理解与设计难度,由于聚合关系建立在抽象层,要求开发者针对抽象进行设计与编程。
桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

适用场景
通过优缺点的分析,我们可以在如下的情形下使用桥接模式:
不想在抽象与其实现之间形成固定的绑定关系;
抽象及其实现都应可以通过子类化独立进行扩展;
对抽象的实现进行修改不应影响客户端代码;
如果每个实现需要额外的子类以细化抽象,则说明有必要把它们分成两个部分;
想在带有不同抽象接口的多个对象之间共享一个实现。

(0)

相关推荐

  • 用代码和UML图化解设计模式之桥接模式的深入分析

    桥接模式就是 独立和实现相分离.不同的厂家生产不同的产品....产品和厂家有这组合的关系. 上代码 复制代码 代码如下: // Bridge.cpp : 定义控制台应用程序的入口点./************************************************************************/   #include "stdafx.h"#include <iostream>using namespace std;class Product

  • C++设计模式之桥接模式

    问题描述 现在要去画一个图形,图形有长方形.圆形和扇形等等:而图形又可以加上不同的颜色,然后,我们就可以画出红色的长方形,绿色的长方形:红色的圆形,绿色的圆形等等.而这种图形的形状在变化,图形的颜色也在变化,当使用代码去实现时,如何面对这种多方面的变化呢?这就要说到今天的桥接模式了. 什么是桥接模式? 对于上述的图形与颜色的问题时,很多时候,我们让各个图形类继承颜色类,比如: 复制代码 代码如下: class CShape { }; class CRectangle : public CShap

  • C#桥接模式完整实例

    本文实例讲述了C#桥接模式实现方法.分享给大家供大家参考.具体如下: C#代码如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { public class HandAddressList:HandSetSoft { public override void Run() { Console.WriteLine

  • 举例讲解Python中的Null模式与桥接模式编程

    Null模式 我想每个人都有一种经历,为了获取某属性,但是有时候属性是None,那么需要你做异常处理, 而假如你想节省这样的条件过滤的代码,可以使用Null模式以减少对象是否为None的判断 python的例子 我举个不是很通用的例子,只是为了让大家理解这个模式:我有很多类, 但是不是每个类都有类方法test,所以我调用类方法就要做个异常处理,类似这样 class A(object): pass class B(object): b = 1 @classmethod def test(cls):

  • 简单了解C#设计模式编程中的桥接模式

    桥接模式的概念 定义:将抽象部分与实现部分分离,使它们都可以独立的变化. 理解:为啦解决一个对象变化而影响多个对象跟着变化,需要把具体实现对象抽象化,使降低对象和变化因素的耦合度,提高系统的可维护性和扩展性. 举例: 手机系统的生态圈问题: 啰嗦点:众所周知wp的生态圈相对与有些系统较差,各位需努力,诺基亚走下神坛,wp要走上神坛,顶一下哈. wp/ios系统类:运行软件,可承载本运行环境下的任何软件,如果新增一个系统,软件就要多做一个系统的版本 weixin/kuwo软件类:开始运行软件,如果

  • 深入解析C#设计模式中对桥接模式的具体运用

    这里以电视遥控器的一个例子来引出桥接模式解决的问题,首先,我们每个牌子的电视机都有一个遥控器,此时我们能想到的一个设计是--把遥控器做为一个抽象类,抽象类中提供遥控器的所有实现,其他具体电视品牌的遥控器都继承这个抽象类,具体设计类图如下: 这样的实现使得每部不同型号的电视都有自己遥控器实现,这样的设计对于电视机的改变可以很好地应对,只需要添加一个派生类就搞定了,但随着时间的推移,用户需要改变遥控器的功能,如:用户可能后面需要对遥控器添加返回上一个台等功能时,此时上面的设计就需要修改抽象类Remo

  • 轻松掌握Java桥接模式

    定义:将抽象部分与实现部分分离,使它们都可以独立的变化. 特点:桥接模式基于类的最小设计原则,通过使用封装,聚合以及继承等行为来让不同的类承担不同的责任.它的主要特点是把抽象(abstraction)与行为实现(implementation)分离开来,从而可以保持各部分的独立性以及应对它们的功能扩展. 企业级开发和常用框架中的应用:多继承的结构 只是从定义理解桥接模式,很难理清它的作用,下面举个例子来说明: 电脑城卖电脑,电脑种类分了平板电脑,笔记本电脑,台式电脑:电脑品牌又分了联想,戴尔,华硕

  • 深入理解JavaScript系列(44):设计模式之桥接模式详解

    介绍 桥接模式(Bridge)将抽象部分与它的实现部分分离,使它们都可以独立地变化. 正文 桥接模式最常用在事件监控上,先看一段代码: 复制代码 代码如下: addEvent(element, 'click', getBeerById); function getBeerById(e) { var id = this.id; asyncRequest('GET', 'beer.uri?id=' + id, function(resp) { // Callback response. consol

  • c#桥接模式(bridge结构模式)用法实例

    本文实例讲述了c#桥接模式(bridge结构模式)用法.分享给大家供大家参考.具体分析如下: 一般来说,Bridge模式使用"对象间的组合关系"解耦了抽象和实现之间固有的绑定关系,使得抽象(Tank的型号)和实现(不同的平台)可以沿着格子的维度来变化.所谓抽象和实现沿着各自维度的变化,即"子类化"它们(比如不同的Tank型号子类,和不同的平台子类),得到各个子类之后,便可以任意组合它们,从而获得不同平台上的不同型号. 在前面的玩家中每增加一个行为,就必须在每个玩家中

  • php设计模式 Bridge (桥接模式)

    复制代码 代码如下: <?php /** * 桥接模式 * * 将抽象部份与它实现部分分离,使用它们都可以有独立的变化 */ abstract class Implementor { abstract public function operation(); } class ConcreteImplementorA extends Implementor { public function operation() { echo "ConcreteImplementorA Operation

随机推荐