桥模式,又叫Bridge模式,和之前的装饰模式有点类似,都属于单一职责模式,不同的是装饰模式的动机是动态的扩展对象的功能,而桥模式是为了应对一个场景中有两个或多个不同维度的变化,桥模式可以弱化他们的耦合性,使得它们可以按照各自的方向扩展。

定义

将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化。 —《设计模式》 GoF

动机

桥接模式的意图是将抽象与实现解耦,使得两者可以独立变化。

桥接模式主要应对的是某个类中有两个或两个以上的维度变化,如果只是用继承则会使设计变得非常臃肿,可以将这些变化维度分离,使它们可以各自变化,在运行时,使用多态机制将这些变化进行组合。

举个具体的例子,比如我们常用播放器,它可以播放MPEG、RMVB、AVI等格式的文件,同时它可以在不同的平台上运行,如windows,linux等,可以使用桥接模式设计该播放器。这里可以把不同的平台看成抽象的部分,不同的格式看成具体的实现部分。

或者是一个绘图软件,我们可以画圆形,正方形,三角形等,同时又需要使用不同的颜色,如红色,绿色,蓝色等,也可以使用桥接模式设计该绘图软件,假设有n种颜色,m种图形,那只需要写n+m个子类就可以达到n*m种组合方式的效果,无需写nxm个类。这里可以把不同的图形看成抽象的部分,不同的颜色看成具体的实现方式。

桥接模式的难度在于需要开发者能正确识别出系统中两个独立变化的维度。

模式分析

  • Abstraction : 定义抽象接口,拥有一个Implementor类型的对象引用
  • RefinedAbstraction:扩展Abstraction中的接口定义,可以有多个
  • Implementor:是具体实现的抽象类
  • ConcreteImplementor:实现Implementor接口,可以有多个

代码分析

实现了一个多平台的播放器,平台相关的设置代码放在不同平台的子类中,不同视频格式的编码算法放在不同的编码子类中,在这里,把不同的平台看成桥接模式的抽象部分,不同的编码方式看成是桥接模式的具体实现方法。

播放器抽象类
播放器抽象类是Abstraction,是一个抽象接口,里面含有Implementor类型的指针对象videoformat,也就是不同的编码方式。

class VideoPlayer
{
public:
	VideoPlayer(VideoFormat* videoformat);
	virtual ~VideoPlayer() {};
	virtual void play() = 0;

	VideoFormat* videoformat;
};

不同的平台
不同的平台是播放器抽象类的子类,具体包括windows平台和unix平台,属于RefinedAbstraction,重写播放器类的play函数,并且在构造函数中初始化编码方式。

class WindowsPlatform : public VideoPlayer
{
public:
	WindowsPlatform(VideoFormat *videoformat);
	~WindowsPlatform();

	virtual void play();
};
WindowsPlatform::WindowsPlatform(VideoFormat *videoformat) : VideoPlayer(videoformat)
{
}

WindowsPlatform::~WindowsPlatform()
{
}

void WindowsPlatform::play()
{
	printf("in windows platform\n");
	this->videoformat->Encode();
}


class UnixPlatform : public VideoPlayer
{
public:
	UnixPlatform(VideoFormat *videoformat);
	~UnixPlatform();

	virtual void play();
};

UnixPlatform::UnixPlatform(VideoFormat *videoformat) : VideoPlayer(videoformat)
{
}

UnixPlatform::~UnixPlatform()
{
}

void UnixPlatform::play()
{
	printf("in unix platform\n");
	this->videoformat->Encode();
}

编码方式抽象类
编码方式抽象类是从播放器中抽出来生成的一个抽象类,也就是桥接模式的Implementor

class VideoFormat
{
public:
	VideoFormat();
	virtual ~VideoFormat();
	virtual void Encode() = 0;
};

不同的编码方式类
不同的编码方式是对编码方式抽象类的扩展和具体实现,包括mpeg方式和avi方式

class Mpeg : public VideoFormat
{
public:
	Mpeg();
	~Mpeg();

	virtual void Encode();
};

void Mpeg::Encode()
{
	printf("start encode mpeg image... \n");
}

class Avi : public VideoFormat
{
public:
	Avi();
	~Avi();

	virtual void Encode();
};

void Avi::Encode()
{
	printf("start encode avi image... \n");
}

main测试函数
main函数中先构造一个mpeg的编码方式,然后动态的加载到播放器中实现

int main()
{
	VideoFormat *videoplatform = new Mpeg;
	VideoPlayer *videoplayer = new WindowsPlatform(videoplatform);
	videoplayer->play();
	return 0;
}

运行结果:

类图

使用场景

  • 一个系统存在两个或多个独立变化的维度,且都需要扩展,对于多个维度的系统,在抽象类中增加其他维度的指针对象即可
  • 不希望因为继承导致类的个数急剧增加的系统
  • 通过桥接模式可以使得抽象角色和具体的角色的搭配有更多的灵活性

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

工厂模式 上一篇
装饰模式 下一篇