【重读设计模式】桥接模式

来源:互联网 发布:程序员眼镜 编辑:程序博客网 时间:2024/04/30 11:30
桥接模式,这个模式使用频率倒是还可以,但是知道这个模式的人却是不多,更多的人可能是会使用这个模式,但是并不知道这个模式。因为这个模式是如此的不被熟知,以至于很多人根本没有听过这个设计模式。下面我们来熟悉这个设计模式。


定义:将抽象部分与它的实现部分分离,使他们都可以独立的变化。又成为柄体模式或接口模式。
定义解释:GOF给桥接模式的这个定义实在是下的不够好,以至于很难理解这句话的含义。可以这样理解,一般我们将系统中容易变化的部分抽象出来(抽象部分),将具体的对象(实现部分)通过继承的方式实现基类的内容,在某些情况下抽象部分(即基类)变的不够稳定需要变化,所以需要继续抽象,这样就破坏了原有的系统设计。桥接模式将抽象部分和实现部分分离,也就是从新设计,将原来的抽象方式改变,变成独立的两部分,彼此可以独立变化。结合例子更容易明白。


使用场景:
(1)在一个系统中,某些事物会有两种或者多维度的变化。
(2)在多个纬度变化时,各自的变化不影响另外的维度。
(3)客户端不感知各个维度的变化。


例子:一般的系统都有日志组件,专门用于打印日志的。一般的都需要有将日志打印到文件(本地日志)和日志打印到网络(网络日志,分布式部署时很有必要,日志可以统一管理)的能力。这时用户可以根据自己的需要,来决定需要打印本地日志还是网络日志。一般的设计是有一个CLog,然后有CNetLog和CFileLog用于实现自己的打印日志函数。现在,日志组件还需要支持可以打印二进制的日志,按照设计一样需要支持本地打印和网络打印,所以现在需要支持四个类,分别是CNetTextLog  CNetBinLog和CFileTextLog  CFileBinLog四个类,分别用于打印网络文本日志,网络二进制日志,本地文本日志和本地二进制日志。这样的设计是有问题的,假如又要支持一种可以支持将日志写入到数据库中,那么又需要支持两个类,而实际上是打印文本还是打印二进制,和实际将这些数据输出到那里是没有什么依赖关系的。通过桥接模式,我们可以解决这种问题。


设计:从例子中可以看到,如果我们基于继承的方法(即CNetTextLog、CNetBinLog、CFileTextLog、CFileBinLog这四个类都继承于CLog)就会造成因为任何维度的变化都会导致子类的爆炸式增长。如果我们不使用完全继承的方法而使用组合,将会得到很好的结果。现在我们将日志打印分为两个维度。
一:打印内容维度,我们定义一个基类CContent用于表示日志的内容,定义CTextContent和CBinContent两个类继承于CContent分别实现分本内容和二进制内容。
二:打印到的位置,我们定义一个基类CLocate用于表示日志的定位位置,定义CFileLocate和CNetLocate两个类继承于CLocate分别实现写入到文件和写入到网络。

我们设计将CLocate内有一个CContent的成员,表示用CLocate来定位输出方向,用CContent来确定输出内容。这样无论是新增一个或者几个内容维度,都只需要新增几个内容维度的类,用于继承于CContent,无论新增一个或者几个打印位置,也只需要新增一个位置的类用于继承CLocate,而这两个维度之间没有相互影响。


类图:



实现:

//============================================================================
// Name        : bridge.cpp
// Author      : tester
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================


#include <iostream>
#include <stdio.h>
using namespace std;


class CContent
{
public:
virtual void get_content() = 0;
virtual ~CContent(){};
};


class CTextContent : public CContent
{
public:
virtual void get_content()
{
printf("i am a text content.\n");
}
};


class CBinContent : public CContent
{
public:
virtual void get_content()
{
printf("i am a bin content.\n");
}
};


class CLocate
{
public:
virtual void write_log() = 0;
virtual ~CLocate(){};
protected:
CContent* m_content;
};


class CFileLocate : public CLocate
{
public:
CFileLocate(CContent* content)
{
m_content = content;
}


void write_log()
{
m_content->get_content();
printf("write to file.\n");
}
};


class CNetLocate : public CLocate
{
public:
CNetLocate(CContent* content)
{
m_content = content;
}


void write_log()
{
m_content->get_content();
printf("write to net.\n");
}
};


int main() {


CContent* content = new CTextContent;
CLocate* locate = new CFileLocate(content);


locate->write_log();


cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
return 0;
}

结果:

i am a text content.
write to file.
!!!Hello World!!!

总结:通过将抽象和实现分离,使他们可以独立的变化而互不影响。在本例子中,CLocate就是抽象,CContent就是实现,CLocate只知道调用CContent的operation方法,而不知道运行期间是那个子类会被实例,同时也不知道自己那个子类会被实例化,这就是抽象和实现分离,这种实现的方式方法就叫做桥接。也就是对于两个本身没有依赖关系的类因为某种特定的场景而产生的has a(有一个)的依赖关系,称之为桥接,就好像将河的两岸架起一座桥而接起来了。

0 0
原创粉丝点击