设计模式(7) 桥接模式(BRIDGE)
来源:互联网 发布:知乎怎么发表想法 编辑:程序博客网 时间:2024/05/12 02:05
问题聚焦:
上一节讲了对象结构型模式,其主要的思想是对象的组合。并接触了一个典型的适配器模式。
这一节再讲一个常用的桥接模式,其主要的思想是抽象部分与实现部分的分离,使得抽象部分与系统平台分离开来,从而实现可移植性。
桥接主要指抽象部分和实现部分之间的关系。
补充一个实例。
意图:
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
别名:Handle/Body
动机:
当一个抽象可能有多个实现时,通常的思路为继承出多个功能类似又各司其职的子类。
继承的方法的缺点是:抽象部分与它的实现部分过分紧密,使得难以对抽象部分和实现部分独立地进行修改、扩充和重用。
考虑下面的这个例子:
一个用户界面工具箱,可移植。同时支持X Window System和IBM的PM系统。运用继承机制,我们可以定义Window抽象类和它的两个子类XWindow与PMWindow,由它们分别实现不同系统平台上的Window界面。
这个实现的不足之处在于:
1) 扩展Window抽象使之适用于不同种类的窗口或新的系统平台很不方便。
假设Window有一个子类IconWindow专门负责图标的处理,为了支持两种平台,必须实现两个新类XIconWindow和PMIconWindow。即如果我们需要支持各种功能的组件,不得不为每种组件实现两种子类。
2)继承机制使得客户代码与平台相关。结果就是,客户代码同时也要依赖当前的平台,会使得客户代也很难移植到其他平台上去。
改进目标:客户在创建代码时,不涉及到其具体实现部分,仅仅是窗口的实现部分依赖于应用运行的平台。这样,客户代码在创建窗口时就不涉及到特定的平台了。
用桥接模式解决这个问题。
- 将Window抽象和它的实现部分分别放在独立的类层次结构中。
- 其中一个类层次结构针对窗口接口。
- 另一个独立的类层次结构针对平台相关的窗口实现部分,这个类层次结构的根类为WindowImp。
对Window子类的所有操作都是用WindowImp接口中的抽象操作实现。这就将窗口的抽象与系统平台相关的实现部分分离开来。因此,我们将Window与WindowImp之间的关系称之为桥接。
适用性:
一下情况使用Bridge模式:
- 你不希望在抽象和它的实现部分之间有一个固定的绑定关系。如在程序运行时刻实现部分应可以被选择或者切换。
- 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这时,桥接模式使你可以对不同的抽象接口和实现部分进行组合,并分别扩充。
- 对一个抽象的实现部分的修改应对客户不产生影响,即客户的挨骂不必重新编译。
- 你想对客户完全隐藏抽象的实现部分,在C++中,类的表示在类接口中是可见的。
- 想在多个对象间共享实现,但同时要求客户并不知道这一点。
参与者:
- Abstraction(Window):定义抽象类的接口;维护一个指向Implementor类型对象的指针;
- RefinedAbstraction(IconWindow):扩充由Abstraction定义的接口
- Implementor(WindowImp):定义实现类的接口,提供基本操作,Abstraction则定义了基于这些基本操作的较高层次的操作。
- ConcreteImplementor(XwindowImp,PMWindowImp):实现Implementor接口并定义它的具体实现。
Abstraction将client的请求转发给它的Implementor对象。
效果:主要说明桥接模式的优点
- 分离接口及其实现部分:抽象类的实现可以在运行时刻进行配置,一个对象甚至可以在运行时刻改变它的实现
- 提高可扩充性
- 实现细节对客户透明
使用桥接模式时,需要注意以下一些问题
1 仅有一个Implementor:在仅有一个实现的时候,没有必要创建一个抽象的Implementor类。
2 创建正确的Implementor:
当存在多个Implementor类的时候,你应该用何种方法,在何时何处确定创建哪一个Implementor类呢?
如果Abstraction知道所有的ConcreteImplementor类,它就可以在它的构造器中对其中的一个类进行实例化,它可以通过传递给构造器的参数确定实例化哪一个类。另外一种方法是首先选择一个缺省的实现,然后根据需要改变这个实现。
也可以代理给另一个对象,由它一次决定。在Window/WindowImp例子中,我们可以引入一个factory对象,该对象的唯一职责就是封装系统平台的细节,这个对象知道应该为所用的平台创建何种类型的WindowImp对象,Window仅需要向它请求一个WindowImp,而它会返回正确类型的WindowImp对象。这种方法的有点是,Abstraction类不和任何一个Implemetor类直接耦合。
3 共享Implementor对象:引入引用计数器机制。
4 采用多重继承机制:在C++中可以使用多重继承机制将抽象接口和它的实现部分结合起来。
代码示例:
下面的代码实现上面的Window/WindowImp的例子。
Window类为客户应用程序定义了窗口抽象类。
class Window{public: Window(View*contents); // requests handled by window virtual void DrawContents(); virtual void Open(); virtual void Close(); virtual void Iconify(); virtual void Deiconify(); // requests forwarded to implementation virtual void SetOrigin(const Points& at); virtual void SetExtent(const Points& extent); virtual void Raise(); virtual void Lower(); virtual void DrawLine(const Point&, const Point&); virtual void DrawRect(const Point&, const Point&); virtual void DrawPolygon(const Point[], int n); virtual void DrawText(const char*, const Points&);protected: WindowImp* GetWindowImp(); View* GetView();private: WindowImp* _imp; View* _contents;};
Window维护一个对WindowImp的引用,WindowImp抽象类定义了一个对底层窗口系统的接口
class WindowImp{public: virtual void ImpTop() = 0; virtual void ImpBottom() = 0; virtual void ImpSetExtent(const Point&) = 0 ; virtual void ImpSetOrigin(const Point&) = 0; virtual void DeviceRect(Coord, Coord, Coord, Coord) = 0; virtual void DeviceText(const char*, Coord, Coord) = 0; virtual void DeviceBitmap(const char*, Cood, Coord) = 0; // lots more functions for drawing on windows...protected: WindowImp();};
Window的子类定义了应用差您工序可能用到的不同类型的窗口,如应用窗口,图标,对话框临时窗口,以及工具箱的移动面板等。
如ApplicationWindow类将实现DrawContents操作以绘制它所存储逇View示例:
class ApplicationWindow : public Window{public: virtual void DrawContents();};void ApplicationWindow::DrawContents(){ GetView()->DrawOn(this);}
又如,IconWindow存储了它所显示的图标对应的位图名。(代码省略)
我们还可以定义许多其他类型的窗口类。
Window的操作由WindowImp的接口定义。
例如,在调用WidowImp来操作在窗口中绘制矩形之前,DrawRect必须从它的两个Point参数中提取四个坐标值。
void Window::DrawRect(const Point& p1,const Point& p2){ WindowImp* imp = GetWindowImp(); imp->DeviceRect(p1.X(), p1.Y(), p2.X(), p2.Y());}
具体的WindowImp子类可支持不同的窗口系统。XwindowImp子类支持XWindow窗口系统。
class XWindowImp : public WindowImp{public: XWindowImp(); virtual void DeviceRect(Coord, Coord, Coord, Coord);private: // lots of X Window system-specific state, including: Display* _dpy; Drawable _winid; GC _gc;};
对于PM系统,我们定义PMWindowImp类。(代码省略)
这些子类用窗口系统的基本操作实现WindowImp的操作
如,X窗口系统这样实现DeviceRect:
void XWindowImp::DeviceRect(){ int x = round(min(x0, x1)); int y = round(min(y0, y1)); int w = round(abs(x0 - x1)); int h = round(abs(x0 - x1)); XDrawRectangle(_dpy, _winid, _gc, x, y, w, h);}
而PM对相同操作的实现可能完全不一样,这使基于平台的实现。
那么一个窗口怎样得到正确而WindowImp子类的实例呢?
可以在Window的GetWindowImp操作中,从一个抽象工厂得到一个正确的实例。
WindowImp * Window::GetWindow() { if (_imp == 0) { _imp = WindowSystemFactory::Instance()->MakeWindowImp(); // WindowSystemFactory::Instance()函数返回一个抽象工厂 // 该工厂负责处理所有与特定窗口系统相关的对象 // 同时,这个抽象工厂实现称了一个单件(单例模式) } return _imp;}
相关模式:
抽象工厂模式可以用来创建和配置一个特定的桥接模式。
适配器模式用来帮助无关的类协同工作。
桥接模式使得抽象接口和实现部分可以独立进行改变。
Demo:
/* * 桥接模式demo * 现实里如果你想要一个android手机,又想要一个iphone的话,只能掏出钱包买两个手机啦,这个可以理解为子类继承 * 但是如果用了桥接模式,就可以在同一个手机安装android系统,或者IOS系统,同一个手机,不同实现,这就是桥接模式*/#include <iostream>class TelephoneImp{public:TelephoneImp() {}virtual void produce() = 0;};class Telephone{public:Telephone() { };void install(TelephoneImp* imp) {_phoneimp = imp;_phoneimp->produce(); };private:TelephoneImp* _phoneimp;};//----------------------------------------------------------class AndroidOS: public TelephoneImp{public:AndroidOS() : TelephoneImp(){}void produce() { std::cout << "You have an Android phone now!" << std::endl;};};class IphoneOS : public TelephoneImp{public:IphoneOS():TelephoneImp(){}void produce(){std::cout << "You have an Iphone now!" << std::endl;}};int main(){std::cout << "-----------------------" << std::endl;std::cout << " 桥接模式Demo" << std::endl;std::cout << "-----------------------" << std::endl;AndroidOS* androidos = new AndroidOS();IphoneOS* iphoneos = new IphoneOS();Telephone* myphone = new Telephone();std::cout << "Installing android os..." << std::endl;myphone->install(androidos);std::cout << std::endl;std::cout << "Installing ios ..." << std::endl;myphone->install(iphoneos);system("Pause");return 0;}
运行截图:
参考资料:
《设计模式:可复用面向对象软件的基础》
1 0
- 设计模式7 - 桥接模式Bridge
- 设计模式(7) 桥接模式(BRIDGE)
- 桥接设计模式bridge
- 设计模式(10)-桥接模式(Bridge)
- 设计模式 Bridge 桥接模式
- Java设计模式-----Bridge桥接模式
- 设计模式--桥接模式(Bridge)
- 设计模式(12)-桥接模式(Bridge)
- 设计模式-----桥接模式(Bridge Pattern)
- 设计模式学习----Bridge桥接模式
- 设计模式之-Bridge-- 桥接模式
- 设计模式 – 桥接模式(Bridge)
- 设计模式之Bridge(桥接模式)
- 设计模式--桥接模式(bridge pattern)
- Java设计模式-----Bridge桥接模式
- 设计模式之Bridge(桥接模式)
- 设计模式-----桥接模式(Bridge Pattern)
- 设计模式-----桥接模式(Bridge Pattern)
- 2分钟搞清楚UML
- Linux OOM-killer(内存不足时kill高内存进程的策略)
- Window7(64位)系统安装Oracle10g
- IOS UIWebView 网页视图
- JSFL:选择元件输出数组
- 设计模式(7) 桥接模式(BRIDGE)
- 位段使用说明
- Redis源码简要分析
- java连接oracle数据库
- 从软件开发说开去
- 1038. Recover the Smallest Number (30)
- Struts2自定义拦截器实例—登陆权限验证
- EBS Form弹性域开发和实现时的概念
- uva753最大流