桥接模式,就是将抽象部分与它的实现部分分离,使他们都可以独立地变化。
假设说有两台电脑(MAC和Lenovo),它们分别安装不同的操作系统(Linux和Windows)。
编写代码实现给电脑安装操作系统。
最简单的办法
首先定义一个操作系统类,定义如下,有LinuxOS类和WindowsOS类。
class OS {
public:
virtual void Install() = 0;
};
class LinuxOS : public OS {
public:
virtual void Install() { cout << "Install Linux OS" << endl; }
};
class WindowsOS : public OS {
public:
virtual void Install() { cout << "Install WindowsOS OS" << endl; }
};
电脑类的定义如下,有MAC类和Lenovo类。
class Computer {
public:
virtual void printInfo() = 0;
};
class MAC : public Computer {
public:
virtual void printInfo() { cout << "This is MAC, "; }
};
class Lenovo : public Computer {
public:
virtual void printInfo() { cout << "This is Lenovo, "; }
};
它们有四种组合:
- MAC + Linux;
- MAC + Windows;
- Lenovo + Linux;
- Lenovo + Windows;
class MACWithLinux : public MAC, public LinuxOS {
public:
void InstallOS() { printInfo(); Install(); }
};
class MACWithWindows : public MAC, public WindowsOS {
public:
void InstallOS() { printInfo(); Install(); }
};
class LenovoWithLinux : public Lenovo, public LinuxOS {
public:
void InstallOS() { printInfo(); Install(); }
};
class LenovoWithWindows : public Lenovo, public WindowsOS {
public:
void InstallOS() { printInfo(); Install(); }
};
在main函数中编写测试代码。
main函数
int main()
{
MACWithLinux a;
a.InstallOS();
MACWithWindows b;
b.InstallOS();
LenovoWithLinux c;
c.InstallOS();
LenovoWithWindows d;
d.InstallOS();
return 0;
}
编译测试,分别实现了给MAC/Lenovo,安装Linux/Windows的功能。
桥接模式
使用上面的方法,如果有N台不同厂家的电脑,M种不同类型的操作系统,那么总共就会有M*N种组合,就要在代码里面定义M*N个派生类,这样维护起来太麻烦了。
是否有更好的方法呢?
答:有,使用桥接模式。
上面的代码中,定义了一个电脑类,一个操作系统类。
我们在电脑类和操作系统类中间建立一个连接(搭一个桥)。
在电脑类里面定义一个指针,指向操作系统类。
首先修改操作系统类和它的派生类,它们的定义与之前相同,只是函数名有一些改变(Install改为InstallOS_impl)。
class OS {
public:
virtual void InstallOS_impl() = 0;
};
class LinuxOS : public OS {
public:
virtual void InstallOS_impl() { cout << "Install Linux OS" << endl; }
};
class WindowsOS : public OS {
public:
virtual void InstallOS_impl() { cout << "Install WindowsOS OS" << endl; }
};
然后修改电脑类,在Computer类里面将需要的函数定义为虚函数。
Computer类
class Computer {
public:
virtual void printInfo() = 0;
virtual void InstallOS() = 0;
};
以MAC类为例,
- 在MAC类中添加一个指向OS类的指针impl,它是一个私有数据成员;
- 增加InstallOS函数,在InstallOS函数中调用对应OS类的InstallOS_impl函数,也就是安装系统的实现函数;
MAC类
class MAC : public Computer {
private:
OS *impl;
public:
MAC(OS *impl) { this->impl = impl; }
virtual void printInfo() { cout << "This is MAC, "; }
virtual void InstallOS() { printInfo(); impl->InstallOS_impl(); }
};
Lenovo类的修改与MAC类相同。
Lenovo类
class Lenovo : public Computer {
private:
OS *impl;
public:
Lenovo(OS *impl) { this->impl = impl; }
virtual void printInfo() { cout << "This is Lenovo, "; }
virtual void InstallOS() { printInfo(); impl->InstallOS_impl(); }
};
在main函数中编写测试代码。
- 分别创建两个操作系统对象os1和os2,分别代表LinuxOS和WindowsOS;
- 然后分别创建电脑类:
如果是MAC,就使用new MAC;如果是Lenovo,就使用new Lenovo;
根据传入的参数决定安装哪种操作系统。
int main()
{
OS *os1 = new LinuxOS();
OS *os2 = new WindowsOS();
Computer *c1 = new MAC(os1);
Computer *c2 = new MAC(os2);
Computer *c3 = new Lenovo(os1);
Computer *c4 = new Lenovo(os2);
c1->InstallOS();
c2->InstallOS();
c3->InstallOS();
c4->InstallOS();
return 0;
}
编译测试,结果符合预期。
此时,如果再加上一个Unix操作系统,加上一个Dell电脑。
那么,代码只需要再加两个类,UnixOS类和Dell类。
UnixOS类
class UnixOS : public OS {
public:
virtual void InstallOS_impl() { cout << "Install Unix OS" << endl; }
};
Dell类
class Dell : public Computer {
private:
OS *impl;
public:
Lenovo(OS *impl) { this->impl = impl; }
virtual void printInfo() { cout << "This is Dell, "; }
virtual void InstallOS() { printInfo(); impl->InstallOS_impl(); }
};
修改main函数。
- 创建一个Unix操作系统对象;
- 分别给MAC,Lenovo,Dell安装Unix;
- 给Dell分别安装MAC,Lenovo,Unix;
测试结果如下,结果符合预期。
如果没有使用桥接模式,新增一个操作系统,一个电脑,需要多定义(3*3 - 2*2 = )5个派生类。
但是使用桥接模式后,只需要多定义两个2派生类,而且使用和维护也都很简单。
总结
最后,总结一下修改。
我们将电脑类定义为了一个抽象类,它含有抽象部分——InstallOS函数。
将操作系统类定义为了一个实现类,它包含实现部分——InstallOS_impl函数。
它们之间通过一个指针impl建立了连接,抽象类会调用实现类中的实现函数InstallOS_impl,来实现操作系统的安装。
而电脑类中仅提供了一个抽象的接口InstallOS,它不负责实现具体的功能。
这样,以后不论实现类有做什么更改,只要接口不变,抽象类都可以不做变动;同理,不论抽象类如何扩展,也不会影响到实现类。
它们互不影响。
impl = implement(实现)
如果看到impl,基本可以确认使用了桥接模式。