转自:http://www.cnblogs.com/rush/archive/2011/06/29/2093743.html  分析十分透彻明了  可以再结合另外一篇文章中的示例理解(http://blog.csdn.net/fly_yr/article/details/8574742)

1.1.1 摘要

在软件系统中,某些类型由于自身的逻辑,它具有两个或两个以上的维度变化,那么如何应对这种“多维度的变化”呢?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度呢?这就是即将要介绍的桥接模式(Bridge)。

使用频率:   medium

  • 定义

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

  • 意图

将抽象与实现解耦。

  • 动机

当一种抽象类型可能有多种实现方式时,一般情况我们可以考虑使用继承来解决抽象类型的多种实现,在抽象类型中定义接口,而子类负责接口的具体实现。但这种做法缺乏灵活性,由于抽象类型和子类之间紧紧地绑定在一起,使得这种关系在运行时不能再修改,这使得它难以修改、扩展和重用不利于抽象和实现解耦,而且这也违背OOP原则:“优先使用对象聚集,而不是继承”。

  • 结构图

图1 桥接模式(Bridge)

  • 参与者

Abstraction

1. 定义抽象接口。

2. 拥有一个Implementor类型对象引用。

  • RefinedAbstraction

1. 扩展Abstraction中的接口定义。

  • Implementor

1. Implementor是具体实现的接口,Implementor和Abstraction接口并不一定完全一致(注:Proxy和ISubject接口一一对应),实际上这两个接口可以完全不一样,Implementor提供具体操作方法,而Abstraction提供更高层次的调用。

  • ConcreteImplementor

1. 实现Implementor接口,给出具体实现。

1.1.2 正文

首先根据桥接模式(Bridge)的定义:将抽象部分与它的实现部分分离,但令我不解的是,怎样才能将抽象与其实现的具体方式分离呢?其实我的迷惑主要是因为误解了实现的含义。这里实现指的是抽象类及其派生类用来实现自定的对象(而不是抽象类的派生类,这些派生类被称为具体类)。不过这样还是难以理解,现在让我们通过具体的例子说明。

我们必需了解桥接模式(Bridge)存在的价值,然后推迟该模式,OK我们通过一个简单的例子说明为什么需要桥接模式(Bridge)吧!

从一个绘制形状的简单问题开始,假设我们接受一个任务:编写一个程序,使用两个绘图程序DrawProgram1和DrawProgram2之一绘制图形(矩形,圆形等),而且我们被告知,实例化图形的时候,它会知道应该使用绘图程序DP1还是DP2。

通过分析我们可以找出抽象类Shape,然后定义Retangle和Circle类继承抽象类Shape,还有就是绘图程序DP1和DP2。

图2绘图程序类图

我们现在初步定义了相关的方法和类,而把Shape,Retangle和Circle都定义为抽象类型方便以后扩展。但我们可以发现图形类型并没有与绘图程序关联起来,而且前面需求中提到图形实例化时候知道具体调用哪个绘图程序。OK那么现在让我们把图形和绘图程序关联起来。

图3绘图程序类图

我们定义了Retangle1和Retangle2继承于抽象类Retangle,添加DrawLine()方法分别调用DP1和DP2的DrawLine()方法,并且Circle1和Circle2中的实现基本相同。我们使用了一种直截了当的方法,实现了两种图形和两个绘图程序的关联。

现在Shape有四个具体类型(Retangle1,Retangle2,Circle1和Circle2),而且每个具体类型都和相应绘图程序对于,但我们要记住“没有不变的需求,世上的软件都改动过三次以上,唯一一个只改动过两次的软件的拥有者已经死了,死在去修改需求的路上”,所以需求总是在不断的变化之中,如果添加新的绘图程序DP3,那么我们就要增加两个具体类型,而且具体类型中调用DP3方法跟之前调用DP1、DP2并没有太大的区别(冗余问题),现在有两种图形(Retangle和Cirle)和三个绘图程序(DP1,DP2和DP3),那么就拥有六种不同Shape(2种图形 * 3个绘图程序),如果我们继续扩展成三种图形那么就具有九种不同Shape(3种图形 * 3个绘图程序),这就会导致“类爆炸”问题。

上面的解决方法,因为抽象类型(Shape)和绘图程序之间是紧耦合,于是存在严重的“类爆炸”问题(每种形状都必须知道自己用哪个绘图程序)。我们需要一种方式将抽象上的变化和实现变化进行解耦。

将抽象与实现解耦这不就是桥接模式(Bridge)的意图吗?在介绍桥接模式(Bridge)之前,我们总结一下前面方式中的问题。

  • 存在冗余
  • 低内聚
  • 紧耦合

由于前面的例子按照不同图形来进行继承的分类,如果我们按照不同绘图程序分类结果又如何呢?OK,那么让我们画出按照不同绘图程序分类类图。

图4绘图程序类图

现在我们继续使用四个类表示现有的图形的组合,但这里我们按照不同绘图程序派生不同图形,所以我们消除了图形类(Retangle1,Retangle2, Circle1和Circle2)和绘图程序的之间的紧耦合,从而消除了它们之间的冗余。现在把耦合转移到更高的继承层次,但问题有出现了当有新的绘图程序加入时,我们的确可以轻松地进行扩展,只要增加ShapeDP3类继承抽象类Shape就OK了,但是要实现一套一模一样的Retangle3和Circle3了。

尽管这种方式对前面的方式有所改进,但冗余和耦合问题依然存在。

在我们每次使用设计模式时,我们应该根据设计原则去设计,而不是直接使用已有设计模式去套用,我们要明白的一点是设计模式是根据一定的设计原则而产生的。

这次我们要遵循两个基本原则:

  • 找出变化封装之
  • 优先使用对象聚集,而不是继承

首先我们可以很快的找出需求中变化:图形和绘图程序,然后我们使用抽象来封装变化就OK了。

图5封装绘图程序中变化

现在我们已经找出了变化图形和绘图程序,注意这里的OperationalDP1和OperationalDP2作为调用绘图程序(DP1和DP2)的接口,因为DP1和DP2是两个已经存在的程序所为我们无法直接抽象出DP1和DP2的高层接口,通过一种间接方式抽象出高层接口,使用抽象类把变化封装在它的“后面”,接着我们就是要在抽象类Shape和DrawingProgramming直接建立依赖关系了(优先使用对象聚集,而不是继承),所以可以通过在其中一个抽象类中保持对方的引用就OK了,但究竟是哪个类依赖于哪个类呢?

这里存在两种情形:

一、DrawProgramming类保存Shape对象引用

二、Shape类保存DrawProgramming对象引用

首先考虑第一种情形,如果在DrawProgramming保存Shape对象引用,我们通过调用Draw()方法绘制图形,但它们必需对Shape类中的图形有所了解(Draw()方法将具体图形封装了),当我们使用OperationalDP1中的DrawCircle()方法时,就要知道Shape中的Circle类型,这违反了对象应该只对自己负责。

图6 情形一

第二种情形,如果Shape对象使用DrawProgramming对象绘制图形时,图形无需知道具体绘图程序。当使用Circle中的Draw()方法我们只需要调用DrawProgramming中的方法DrawCircle(),因此可以让Shape保存DrawProgramming的对象引用。

图6 情形二

通过分析我们可以确定采用情形二实现起来相对简单,接着在抽象类Shape增加DrawProgramming对象引用,从而在Shape和DrawProgramming之间建立了一种聚集关系(has – a关系)。

现在让我们回忆一下桥接模式(Bridge)的定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化。Shape类及其实现就是抽象部分,而DrawingProgramming及其实现是具体部分,通过聚集使得它们分离开来,从而应对变化和扩展更加灵活。

图7 桥接模式实现绘图程序

我们已经完成了绘图程序的类设计,而且通过该程序类设计我们了解到了桥接模式(Bridge)的作用,现在让我们通过具体代码完成绘图程序。

 /// <summary>
/// As Abstraction.
/// </summary>
public abstract class Shape
{
/// <summary>
/// Has a reference from DrawingProgramming.
/// </summary>
private DrawingProgramming _dp; public Shape()
{
} public Shape(DrawingProgramming dp)
{
this.Dp = dp;
} public abstract void Draw(); public DrawingProgramming Dp
{
get { return _dp; }
set { _dp = value; }
} } /// <summary>
/// As Refined Abstraction.
/// </summary>
public class Retangle : Shape
{
private int _width = ;
private int _height = ; public Retangle(DrawingProgramming dp, int width, int height)
: base(dp)
{
this.Width = width;
this.Height = height;
} public override void Draw()
{
this.DrawRetangle(Width, Height);
} public void DrawRetangle(int width, int height)
{
this.Dp.DrawRetangle(width, height);
} public int Width
{
get { return _width; }
set { _width = value; }
} public int Height
{
get { return _height; }
set { _height = value; }
} } /// <summary>
/// As Refined Abstraction.
/// </summary>
public class Triangle : Shape
{
private int _rows = ; public Triangle(DrawingProgramming dp, int rows)
: base(dp)
{
this.Rows = rows;
} public override void Draw()
{
this.DrawTriangle(this.Rows);
} public void DrawTriangle(int rows)
{
this.Dp.DrawTriangle(rows);
} public int Rows
{
get { return _rows; }
set { _rows = value; }
}
} /// <summary>
/// As Implementor.
/// </summary>
public abstract class DrawingProgramming
{
/// <summary>
/// Abstract draw method.
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
public abstract void DrawRetangle(int width, int height); public abstract void DrawTriangle(int Rows);
} /// <summary>
/// As concrete Implementor
/// </summary>
public class OperationalDP1 : DrawingProgramming
{
public OperationalDP1()
{
} public DP1 DP1
{
get
{
throw new System.NotImplementedException();
}
set
{
}
} public override void DrawRetangle(int width, int height)
{
DP1.DrawRetangle(width, height);
} public override void DrawTriangle(int Rows)
{
DP1.DrawTriangle(Rows);
}
} /// <summary>
/// As concrete Implementor
/// </summary>
public class OperationalDP2 : DrawingProgramming
{ public OperationalDP2()
{
} public DP2 DP2
{
get
{
throw new System.NotImplementedException();
}
set
{
}
} public override void DrawRetangle(int width, int height)
{
DP2.DrawRetangle(width, height);
} public override void DrawTriangle(int Rows)
{
DP2.DrawTriangle(Rows);
}
} /// <summary>
/// existed drawing programming.
/// </summary>
public class DP1
{
/// <summary>
/// Concrete draw method.
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
public static void DrawRetangle(int width, int height)
{
for (int i = ; i < height; i++)
{
for (int j = ; j < width; j++)
{
Console.Write("■");
}
Console.WriteLine();
}
} public static void DrawTriangle(int Rows)
{
for (int i = ; i < Rows; i++)
{
for (int j = ; j < i + ; j++)
{
Console.Write("■");
}
Console.WriteLine();
}
}
} /// <summary>
/// existed drawing programming.
/// </summary>
public class DP2
{
/// <summary>
/// Concrete draw method.
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
public static void DrawRetangle(int width, int height)
{
for (int i = ; i < height; i++)
{
for (int j = ; j < width; j++)
{
Console.Write("★");
}
Console.WriteLine();
}
} public static void DrawTriangle(int Rows)
{
for (int i = ; i < Rows; i++)
{
for (int j = ; j < i + ; j++)
{
Console.Write("★");
}
Console.WriteLine();
}
}
}

图8 绘图程序

通过上面的例子我们对桥接模式(Bridge)有了初步的了解,假设我们的绘图程序DP1拥有特有擦除方法Wipe(),从而需要在OperationalDP1类中增加相应的方法,但没有必要修改DrawingProgramming类,现在问题出现了我们要在抽象类Shpe中增加Wipe()方法,但这种修改是我们愿意看到的。这时我们可以考虑以下两种方法解决问题。

方法一:在基类中添加虚的方法,然后需要的子类重写基类的虚方法。

方法二:使用.NET Framework中的扩展方法,对基类方法进行扩展。

1.1.3 总结

桥接模式(Bridge)优点:

将实现予以解耦,让它和界面之间不再永久绑定。

抽象和实现可以独立扩展,不会影响到对方。

对于具体实现的修改,不会影响到客户端。

桥接模式(Bridge)缺点:

增加了设计复杂度。

抽象类的修改影响到子类。

桥接模式(Bridge)用途:

适用在需要跨多平台的图形和窗口系统。

当需要用不同的方式改变接口和实现时。

通过上述的介绍,我们了解为什么需要桥接模式(Bridge)和如何使用桥接模式(Bridge),由于对象的多维度的变化,使得难以决定变化时,我们可以把对象和变化抽象出来。

如果我们的对象依赖于抽象,对于具体的实现并不关心,我们可以通过对象组合,组合出我们想要的对象。桥接模式符合OCP(对于扩展开发,对于修改关闭)设计模式的原则。

关于作者:

[作者]:JK_Rush从事.NET开发和热衷于开源高性能系统设计,通过博文交流和分享经验,欢迎转载,请保留原文地址,谢谢。
[出处]: http://www.cnblogs.com/rush/ 
[本文基于]: 署名-非商业性使用 3.0 许可协议发布,欢迎转载,演绎,但是必须保留本文的署名 JK_Rush (包含链接),且不得用于商业目的。如您有任何疑问或者授权方面的协商,请与我联系 。

转:桥接模式(Bridge)的更多相关文章

  1. 桥接模式(Bridge Pattern)

    1,定义           桥接模式(Bridge Pattern),也称为桥梁模式,其用意是将抽象化与实现化脱耦,使得两者可以独立的变化,它可以使软件系统沿着多个方向进行变化,而又不引入额外的复杂 ...

  2. c#桥接模式(bridge结构模式)

    桥接模式(bridge结构模式)c#简单例子 在前面的玩家中每增加一个行为,就必须在每个玩家中都增加,通过桥接模式将行为提取出来了,减少变化 ? 1 2 3 4 5 6 7 8 9 10 11 12 ...

  3. 乐在其中设计模式(C#) - 桥接模式(Bridge Pattern)

    原文:乐在其中设计模式(C#) - 桥接模式(Bridge Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 桥接模式(Bridge Pattern) 作者:webabcd 介绍 ...

  4. 【设计模式】桥接模式 Bridge Pattern

    开篇还是引用吕振宇老师的那篇经典的文章<设计模式随笔-蜡笔与毛笔的故事>.这个真是太经典了,没有比这个例子能更好的阐明桥接模式了,这里我就直接盗来用了. 现在市面上卖的蜡笔很多,各种型号, ...

  5. 桥接模式-Bridge(Java实现)

    桥接模式-Bridge 桥梁模式的用意是"将抽象化(Abstraction)与实现化(Implementation)脱耦, 将"类的功能层次结构" 与 "类的实 ...

  6. 二十四种设计模式:桥接模式(Bridge Pattern)

    桥接模式(Bridge Pattern) 介绍将抽象部分与它的实现部分分离,使它们都可以独立地变化. 示例有一个Message实体类,对它的操作有Insert()和Get()方法,现在使这些操作的抽象 ...

  7. python 设计模式之桥接模式 Bridge Pattern

    #写在前面 前面写了那么设计模式了,有没有觉得有些模式之间很类似,甚至感觉作用重叠了,模式并不是完全隔离和独立的,有的模式内部其实用到了其他模式的技术,但是又有自己的创新点,如果一味地认为每个模式都是 ...

  8. 桥接模式(Bridge、Implementor)(具体不同平台日志记录,抽象与实现分离)

    桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化.它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式 ...

  9. 桥接模式/bridge模式/对象结构型

    意图 将抽象部分与它的实现部分分离,使它们都可以独立的变化. 动机 当一个抽象类有多个实现时,通常用继承来协调它们.但是继承机制将抽象和实现固定,难以对抽象部分和实现部分独立地进行修改.扩充和重用. ...

  10. Net设计模式实例之桥接模式( Bridge Pattern)

    一.桥接模式简介(Brief Introduction) 桥接模式(Bridge Pattern),将抽象部分与它的实现部分分离,使的抽象和实现都可以独立地变化. Decouple an abstra ...

随机推荐

  1. 最简单的C/S程序——让服务器来做加法

    还在写“Hello world!”式的单机程序吗?还在各种拖控件吗?是否自己都觉得有点low呢?来个质的飞跃吧!看看怎么让服务器帮咱做加法! 所谓C/S程序就是Client/Server程序,自然既包 ...

  2. jqeury之平移轮播

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. 如何将vs2012项目的网站布置到iis上,实现内网访问

    1首先获得你本机的ip地址 可以通过命令行输入 ipconfig/all 2配置电脑的iis(前提是你已经安装了) 右击我的电脑选择管理 右键网站添加网页 会出来上面的对话框 选择直接的项目web路径 ...

  4. Unity脚本在层级面板中的执行顺序测试2

    上一篇测试了生成顺序对执行顺序的影响,链接:LINK 执行顺序测试3: LINK 这篇主要测试一下Awake,OnEnable,Start三个常用消息的循环顺序 1.测试消息循环顺序 先上一个最简单的 ...

  5. C#获得枚举类型的长度

    enum MyEnum { Value1, Value2, } class Program { static void Main(string[] args) { var e = new MyEnum ...

  6. C++的几种强制类型转换

    有时我们希望显式地将对象强制类型转换成另外一种类型.例如,如果想在下面的代码中执行浮点数除法: int i, j; double slope = i / j; 就要使用某种方法将i和/或j显式地转换成 ...

  7. view class source code with JAD plugin in Eclipse

    The default class viewer doesn't decompile the class file so you cannot open and check the source co ...

  8. uva 10673 Play with Floor and Ceil

    Problem APlay with Floor and CeilInput: standard inputOutput: standard outputTime Limit: 1 second Th ...

  9. js遍历

    最近看了一些不错的文章关于js遍历+js数组去重+文件上传的,今天也自己动手试了试.有好多之前不是细节不是很了解.正好学习了. map函数也是 类似这样的对象还有函数的属性arguments对象,当然 ...

  10. 2016CCPC东北地区大学生程序设计竞赛 1003 HDU5924

    链接http://acm.hdu.edu.cn/showproblem.php?pid=5924 题意:根据公式求C,D 解法:打表找规律 #include <bits/stdc++.h> ...