你什么时候使用桥梁模式? 与适配器模式有什么不同?
有没有人在现实世界的应用程序中使用桥梁模式 ? 如果是这样,你是怎么使用它的? 是我吗,还是仅仅是一个适配器模式与一点点依赖性注入混合注入? 它真的值得拥有自己的模式吗?
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,但是如果你使用现有的设备驱动程序,并且把一个包装类提供统一的界面。
所以在代码方面,这两种模式非常相似。 在商业方面,他们是不同的。
根据我的经验,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
请注意,可以将新颜色和形状轻松添加到系统中,而不会由于排列而导致子类爆炸。
桥接和适配器的意图是不同的,我们需要两个模式分开。
桥梁模式:
- 这是一个结构模式
- 抽象和实现在编译时不受限制
- 抽象和实施 – 两者都可以在客户端没有影响
- 使用inheritance的组合。
在以下情况下使用Bridge模式:
- 你想要实现的运行时绑定,
- 你有一个由耦合接口和众多实现产生的类的泛滥,
- 你想在多个对象之间共享一个实现,
- 您需要映射正交类层次结构。
@ John Sonmez的回答清楚地表明了桥梁模式在减less阶级层次上的有效性。
您可以参考下面的文档链接,以更好地了解桥梁模式与代码示例
适配器模式 :
- 它允许两个不相关的接口通过不同的对象一起工作 ,可能扮演相同的angular色。
- 它修改原始界面。
主要区别:
- 适配器在devise完成后就可以使用。 桥在他们之前使他们工作。
- 预先devise网桥 ,使抽象和实现独立变化 。 适配器进行了改造,使无关的类一起工作。
- 意图: 适配器允许两个不相关的接口一起工作。 桥允许抽象和实现独立变化。
相关的SE问题与UML图和工作代码:
桥接模式和适配器模式之间的区别
有用的文章:
源造桥模式文章
源代码适配器模式文章
journaldev桥模式文章
编辑:
桥梁模式真实世界的例子(根据meta.stackoverflow.combuild议,纳入文档网站的例子在这个post,因为文件将要日落)
桥接模式将抽象从实现中分离出来,以便两者可以独立地变化。 它是通过构图而不是inheritance来实现的。
来自维基百科的桥梁模式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
说明:
-
Vehicle
是一个抽象。 -
Car
和Truck
是Vehicle
两个具体实现。 -
Vehicle
定义了一个抽象方法:addGear()
。 -
Gear
是实现者界面 -
ManualGear
和AutoGear
是Gear
两个实现 -
Vehicle
包含implementor
接口而不是实现接口。 实现者接口的Compositon
是这种模式的关键: 它允许抽象和实现独立地变化。 -
Car
和Truck
定义抽象的实现(重新定义抽象):addGear()
:它包含Gear
–Manual
或Auto
Bridge模式的使用案例 :
- 抽象和实现可以彼此独立地变化,并且在编译时不受限制
- 映射正交层次结构 – 一个用于抽象 ,一个用于实现 。
对我来说,我认为它是一个机制,你可以交换接口。 在现实世界中,你可能有一个可以使用多个界面的类,Bridge可以让你交换。