一、 桥接模式概述
在软件系统中,有些类具有两个或多个变化维度,如一个跨平台的日志记录类,它有多种日志输出方式(XML文件、数据库文件),同时还能支持多种操作系统。那么其输出方式就是一个变化维度,而支持的操作系统又是一个变化维度。那么我们如何来设计这样的一个类呢?我们可以定义两个类,一个日志类,它能够支持多种操作系统,另一个是输出方式类,它定义了多种输出方式。然后在日志类中定义一个输出方式类的对象,这样日志类就可以方便的调用输出方式类中定义的输出方法了。这就是一个简易的桥接模式实例模型,桥接模式可以降低系统的复杂性。下面是这个实例的类图:
其实现代码如下:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class OutPut
{
public:
virtual void outPutMethod() = 0;
};
class OutInXML : public OutPut
{
public:
virtual void outPutMethod()
{
cout << "outPut in xml file." << endl;
}
};
class OutInDatabase : public OutPut
{
public:
virtual void outPutMethod()
{
cout << "outPut in database file." << endl;
}
};
class ActivityLog
{
protected:
OutPut *outPut;
public:
void SetOutPut(OutPut *outObj)
{
outPut = outObj;
}
virtual void record() = 0;
};
class LinuxLog : public ActivityLog
{
public:
virtual void record()
{
cout << "Linux Log record ";
outPut->outPutMethod();
}
};
class WindowsLog : public ActivityLog
{
public:
virtual void record()
{
cout << "Windows Log record ";
outPut->outPutMethod();
}
};
int main()
{
ActivityLog *aLog;
aLog = new LinuxLog();
aLog->SetOutPut(new OutInXML());
aLog->record();
aLog = new WindowsLog();
aLog->SetOutPut(new OutInDatabase());
aLog->record();
cout << endl;
return 0;
}
二、 桥接模式结构分析
从上面的图中我们可以看到,桥接模式中有一条连接两个继承结构的桥,下面让来仔细分析一下桥模式都有哪些角色及模式的特点。首先让我们来看一下桥接模式的典型结构图。
从图上可以看到,桥接模式主要有这些角色:
(1)、Abstraction(抽象类):用于定义抽象类的接口,其中定义了一个Implementor类型的对象,可以通过此对象调用Implementor类中定义的业务方法。
(2)、RefinedAbstraction(扩充抽象类):扩充由Abstraction定义的接口,并实现其中的业务方法。
(3)、Implementor(实现类接口):在此接口中仅提供基本操作的定义,具体实现是由子类去定义的,由于Abstraction类中有一个Implementor类的对象,所以Abstraciton的子类可以方便的调用Implementor中定义的方法,这样就可以使用关联关系来替代继承关系了。
(4)、ConcreteImplementor(具体实现类):实现Implementor接口的方法,并提供各自不同的操作方法,在运行时可替换父类对象,给客户端提供具体的业务操作方法。
桥接模式体现了很多面向对象的设计原则,包括开闭原则、合成复用原则、里氏代换原则、依赖倒转原则等。它减少了系统中子类的个数,如要设计一个跨平台的图片处理系统,如果有n个系统,m种图片格式,则用继承的方式实现需要(n * m)个子类,而用桥接模式只需要(n + m)个子类。所以桥接模式往往会使代码更简洁,生成的可执行程序文件更小。在桥接模式中,有三个非常重要的概念:抽象化、实现化、解耦合。
(1)抽象化:其实就是面向对象中的类的概念,抽象化过程就是把不同实体的相同性质抽取出来形成类的过程。
(2)实现化:也就是类的实例,即类的实例化,抽象化和实现化是一对互逆的过程。
(3)解耦合:就是将抽象化和实现化之间的耦合解开的过程,桥接模式中的解耦是通过关联关系来实现的,即在抽象化和实例化中使用关联关系而不是用继承关系,使两者可以相对独立的修改。如果使用继承关系实现,那么父类的修改对象子类的影响往往是很大的,即父类与子类的耦合比较大。
三、 桥接模式实例
假设我们需要设计这一些游戏角色,它们拥有的套装各类是相同的,而且可以根据需要换上不同的套装。为了简单起见,在此规定只有两种角色,三种可选择套装吧。那么我们如何实现这个设计呢?如果我们用继承来实现,显然我们需要为每个角色的每种着装都写一个实现类,这样就需要6个子类,如果角色和可选择套装增加,那么需要的子类个数就会成倍的增加(n * m数量级)。但是如果我们用关联关系,那么子类的个数就会减少很多(n + m数量级),而且系统的可扩展性很好。下面是这个设计的类图:
1. Role(角色类):
public abstract class Role {
protected Suit suit;
public void setSuit(Suit suitobj)
{
this.suit = suitobj;
}
public abstract void showSuit(String suitType);
}
2. Lion(具体角色类):
public class Lion extends Role{
@Override
public void showSuit(String suitType) {
String roleName = "Lion";
this.suit.wear(roleName, suitType);
}
}
3. Hawking(具体角色类):
public class Hawking extends Role{
@Override
public void showSuit(String suitType) {
String roleName = "Hawking";
this.suit.wear(roleName, suitType);
}
}
4. SuitInterface(套装接口):
public interface SuitInterface {
void wear(String roleName, String suitType);
}
5. ShapaeshiftingSuit(具体套装类):
public class ShapeshiftingSuit implements SuitInterface{
@Override
public void wear(String roleName, String suitType) {
System.out.println(roleName + " wear " + suitType);
}
}
6. MagicReinforceSuit(具体套装类):
public class MagicReinforceSuit implements SuitInterface{
@Override
public void wear(String roleName, String suitType) {
System.out.println(roleName + " wear " + suitType);
}
}
7. GorgeousSuit(具体套装类):
public class GorgeousSuit implements SuitInterface{
@Override
public void wear(String roleName, String suitType) {
System.out.println(roleName + " wear " + suitType);
}
}
8. 测试类
public class ClientTest {
public static void main(String argv[])
{
SuitInterface suit = new ShapeshiftingSuit();
Role role = new Hawking();
role.setSuit(suit);
role.showSuit("MagicReinforceSuit");
role.showSuit("ShapeshiftingSuit");
}
}
四、小结
桥接模式分离抽象接口及其实现部分,由于桥接模式使用关系来对抽象和实现之间解耦,所以抽象和实现可以自由的修改,而且子类可以任意组合,这样就可以高效方便的得到多种组合对象;采用桥接模式,会使系统的可扩充性更好,因为抽象和实现耦合低,所以两者可以独立修改,可以方便的增加具有不同功能的子类,而不用修改原系统。
但是,桥接模式要求必须明确系统中的多个独立变化的维度,所以其使用范围有一定的局限性,而且它会增加系统的可读性和设计难度。