你什么时候使用桥梁模式? 与适配器模式有什么不同?

有没有人在现实世界的应用程序中使用桥梁模式 ? 如果是这样,你是怎么使用它的? 是我吗,还是仅仅是一个适配器模式与一点点依赖性注入混合注入? 它真的值得拥有自己的模式吗?

Bridge模式的一个经典示例用于UI环境中形状的定义(请参阅Bridge模式维基百科条目 )。 桥梁模式是模板和策略模式的组合 。

桥接模式中适配器模式的一些方面是常见的。 不过,引用这篇文章 :

初看起来,Bridge模式看起来很像Adapter模式,因为一个类用于将一种接口转换为另一种接口。 然而,Adapter模式的目的是使一个或多个类的接口看起来与特定类相同。 Bridge模式旨在将类的接口与其实现分开,以便在不更改客户机代码的情况下更改或replace实现。

桥梁模式是旧build议的应用,“更喜欢构成而不是inheritance”。 当你必须以相互正交的方式分类不同的时间时,它变得方便。 假设您必须实现彩色形状的层次结构。 你不会用Rectangle和Circle子类化Shape,然后用RedRectangle,BlueRectangle和GreenRectangle子类化Rectangle,Circle也是一样。 你更喜欢说每个形状都有一个颜色,并实现一个颜色层次,这就是桥梁模式。 那么,我不会实现“色彩层次”,但你明白了…

费德里科和约翰的答案相结合。

什么时候:

----Shape--- / \ Rectangle Circle / \ / \ BlueRectangle RedRectangle BlueCircle RedCircle 

重构为:

  ----Shape--- Color / \ / \ Rectangle(Color) Circle(Color) Blue Red 

什么时候:

  A / \ Aa Ab / \ / \ Aa1 Aa2 Ab1 Ab2 

重构为:

  AN / \ / \ Aa(N) Ab(N) 1 2 

适配器和桥接当然是相关的,区别是微妙的。 一些认为自己正在使用这些模式的人实际上正在使用其他模式。

我已经看到的解释是,当你试图统一一些已经存在的不兼容类的接口时,使用了Adapter。 适配器的function是作为一种可以被认为是遗留的实现的翻译器。

Bridge模式用于更可能是绿地的代码。 您正在deviseBridge以为需要改变的实现提供抽象接口,但是您还要定义这些实现类的接口。

设备驱动程序是一个经常被引用的Bridge的例子,但是如果你为设备供应商定义了接口规范的话,我会说它是一个Bridge,但是如果你使用现有的设备驱动程序,并且把一个包装类提供统一的界面。

所以在代码方面,这两种模式非常相似。 在商业方面,他们是不同的。

另见http://c2.com/cgi/wiki?BridgePattern

根据我的经验,Bridge是一个经常出现的模式,因为只要域中有两个正交维度就是解决scheme。 例如形状和绘图方法,行为和平台,文件格式和序列化程序等等。

而且一个build议:总是从概念的angular度思考devise模式,而不是从实现的angular度来思考。 从正确的angular度来看,Bridge不能和Adapter混淆,因为它们解决了一个不同的问题,组合不仅仅是因为它本身,而是因为它允许分开处理正交关注。

我在工作中使用了桥梁模式。 我用C ++编程,在那里经常被称为PIMPL成语(指向执行)。 它看起来像这样:

 class A { public: void foo() { pImpl->foo(); } private: Aimpl *pImpl; }; class Aimpl { public: void foo(); void bar(); }; 

在这个例子中, class A包含接口, class Aimpl包含实现。

这种模式的一个用途是只公开一些实现类的公共成员,而不是其他的。 在这个例子中,只能通过A的公共接口调用Aimpl::foo() ,但不能Aimpl::bar()

另一个优点是你可以在一个单独的头文件中定义Aimpl ,这个头文件不需要被A的用户包含。 您Aimpl在定义A之前使用Aimpl前向声明,并将所有引用pImpl的成员函数的定义移动到.cpp文件中。 这使您能够保持Aimpl标头私有,并缩短编译时间。

在代码中放置形状示例:

 #include<iostream> #include<string> #include<cstdlib> using namespace std; class IColor { public: virtual string Color() = 0; }; class RedColor: public IColor { public: string Color() { return "of Red Color"; } }; class BlueColor: public IColor { public: string Color() { return "of Blue Color"; } }; class IShape { public: virtual string Draw() = 0; }; class Circle: public IShape { IColor* impl; public: Circle(IColor *obj):impl(obj){} string Draw() { return "Drawn a Circle "+ impl->Color(); } }; class Square: public IShape { IColor* impl; public: Square(IColor *obj):impl(obj){} string Draw() { return "Drawn a Square "+ impl->Color();; } }; int main() { IColor* red = new RedColor(); IColor* blue = new BlueColor(); IShape* sq = new Square(red); IShape* cr = new Circle(blue); cout<<"\n"<<sq->Draw(); cout<<"\n"<<cr->Draw(); delete red; delete blue; return 1; } 

输出是:

 Drawn a Square of Red Color Drawn a Circle of Blue Color 

请注意,可以将新颜色和形状轻松添加到系统中,而不会由于排列而导致子类爆炸。

桥接适配器的意图是不同的,我们需要两个模式分开。

桥梁模式:

  1. 这是一个结构模式
  2. 抽象和实现在编译时不受限制
  3. 抽象和实施 – 两者都可以在客户端没有影响
  4. 使用inheritance的组合。

在以下情况下使用Bridge模式:

  1. 你想要实现的运行时绑定,
  2. 你有一个由耦合接口和众多实现产生的类的泛滥,
  3. 你想在多个对象之间共享一个实现,
  4. 您需要映射正交类层次结构。

@ John Sonmez的回答清楚地表明了桥梁模式在减less阶级层次上的有效性。

您可以参考下面的文档链接,以更好地了解桥梁模式与代码示例

适配器模式

  1. 允许两个不相关的接口通过不同的对象一起工作 ,可能扮演相同的angular色。
  2. 它修改原始界面。

主要区别:

  1. 适配器在devise完成后就可以使用。 在他们之前使他们工作。
  2. 预先devise网桥 ,使抽象和实现独立变化适配器进行了改造,使无关的类一起工作。
  3. 意图: 适配器允许两个不相关的接口一起工作。 允许抽象和实现独立变化。

相关的SE问题与UML图和工作代码:

桥接模式和适配器模式之间的区别

有用的文章:

源造桥模式文章

源代码适配器模式文章

journaldev桥模式文章

编辑:

桥梁模式真实世界的例子(根据meta.stackoverflow.combuild议,纳入文档网站的例子在这个post,因为文件将要日落)

桥接模式将抽象从实现中分离出来,以便两者可以独立地变化。 它是通过构图而不是inheritance来实现的。

来自维基百科的桥梁模式UML:

来自维基百科的桥梁模式UML

这种模式中有四个组件。

Abstraction :它定义了一个接口

RefinedAbstraction :它实现抽象:

Implementor :它定义了一个实现的接口

ConcreteImplementor实现:实现Implementor接口。

The crux of Bridge pattern :两个正交的使用合成(并且没有inheritance)的类层次结构。 抽象层次结构和实现层次结构可以独立变化。 实现从来没有提到抽象。 抽象包含实现接口作为成员(通过组合)。 这个组合减less了一层inheritance层次。

真实的字用例:

使不同的车辆有两种版本的手动和自动齿轮系统。

示例代码:

 /* Implementor interface*/ interface Gear{ void handleGear(); } /* Concrete Implementor - 1 */ class ManualGear implements Gear{ public void handleGear(){ System.out.println("Manual gear"); } } /* Concrete Implementor - 2 */ class AutoGear implements Gear{ public void handleGear(){ System.out.println("Auto gear"); } } /* Abstraction (abstract class) */ abstract class Vehicle { Gear gear; public Vehicle(Gear gear){ this.gear = gear; } abstract void addGear(); } /* RefinedAbstraction - 1*/ class Car extends Vehicle{ public Car(Gear gear){ super(gear); // initialize various other Car components to make the car } public void addGear(){ System.out.print("Car handles "); gear.handleGear(); } } /* RefinedAbstraction - 2 */ class Truck extends Vehicle{ public Truck(Gear gear){ super(gear); // initialize various other Truck components to make the car } public void addGear(){ System.out.print("Truck handles " ); gear.handleGear(); } } /* Client program */ public class BridgeDemo { public static void main(String args[]){ Gear gear = new ManualGear(); Vehicle vehicle = new Car(gear); vehicle.addGear(); gear = new AutoGear(); vehicle = new Car(gear); vehicle.addGear(); gear = new ManualGear(); vehicle = new Truck(gear); vehicle.addGear(); gear = new AutoGear(); vehicle = new Truck(gear); vehicle.addGear(); } } 

输出:

 Car handles Manual gear Car handles Auto gear Truck handles Manual gear Truck handles Auto gear 

说明:

  1. Vehicle是一个抽象。
  2. CarTruckVehicle两个具体实现。
  3. Vehicle定义了一个抽象方法: addGear()
  4. Gear是实现者界面
  5. ManualGearAutoGearGear两个实现
  6. Vehicle包含implementor接口而不是实现接口。 实现者接口的Compositon是这种模式的关键: 它允许抽象和实现独立地变化。
  7. CarTruck定义抽象的实现(重新定义抽象): addGear() :它包含GearManualAuto

Bridge模式的使用案例

  1. 抽象实现可以彼此独立地变化,并且在编译时不受限制
  2. 映射正交层次结构 – 一个用于抽象 ,一个用于实现

对我来说,我认为它是一个机制,你可以交换接口。 在现实世界中,你可能有一个可以使用多个界面的类,Bridge可以让你交换。