Design/bridge/readme.md
2024-10-28 14:07:29 +08:00

156 lines
4.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 介绍
桥接模式应该是比较难理解的模式之一, 但是代码的实现相对简单.
对于这个模式可能有两种不同的理解方式
在GoF的《设计模式》一书中桥接模式是这么定义的
`“Decouple an abstraction from its implementation so that the two can vary independently。”`
翻译成中文就是:
`“将抽象和实现解耦,让它们可以独立变化。”`
另外一种理解方式:
`“一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。”`
通过组合关系来替代继承关系, 避免继承层次的指数级爆炸. 这种理解方式非常类似于`组合优于继承`设计原则.
## 原理
就以手机, 和游戏进行举例
手机可以有多个品牌, 可以有华为, 小米, 苹果….
游戏有多个种类, 射击, 冒险, 闯关…
华为可以玩 射击, 冒险, 小米可以玩 冒险, 闯关… 可以有很多种组合
**游戏基类**
```cpp
class Game {
std::string name;
public:
Game(std::string name) : name(name) {}
virtual ~Game() = default;
virtual void Play() = 0;
void print() { std::cout << name << std::endl; }
};
```
**实现**
```cpp
class RunningGame : public Game {
public:
RunningGame() : Game("Running") {}
void Play() override { std::cout << "Play Running Game." << std::endl; }
};
class ShootingGame : public Game {
public:
ShootingGame() : Game("Shooting") {}
void Play() override { std::cout << "Play Shooting Game." << std::endl; }
};
```
**手机基类**
```cpp
class Phone {
std::shared_ptr<Game> game;
public:
Phone() {}
virtual ~Phone() = default;
virtual void Download(std::shared_ptr<Game> game) = 0;
virtual void Open() = 0;
};
```
**实现**
```cpp
class iPhone : public Phone {
std::shared_ptr<Game> game;
public:
void Download(std::shared_ptr<Game> game) override {
std::cout << "iPhone. download ";
game->print();
this->game = game;
}
void Open() override {
std::cout << "iPhone. start ";
game->Play();
}
};
class Samsung : public Phone {
std::shared_ptr<Game> game;
public:
void Download(std::shared_ptr<Game> game) override {
std::cout << "Samsung. download ";
game->print();
this->game = game;
}
void Open() override {
std::cout << "Samsung. start ";
game->Play();
}
};
```
## 调用方式
```cpp
//有两个游戏
auto running = std::make_shared<RunningGame>();
auto shooting = std::make_shared<ShootingGame>();
//两个手机
auto iphone = std::make_shared<iPhone>();
auto samsung = std::make_shared<Samsung>();
//下载游戏
iphone->Download(running);
samsung->Download(shooting);
//运行游戏
iphone->Open();
samsung->Open();
//换个游戏
samsung->Download(running);
//运行游戏
samsung->Open();
```
## **效果**
```cpp
./bin/design/Bridge
iPhone. download Running
Samsung. download Shooting
iPhone. start Play Running Game.
Samsung. start Play Shooting Game.
Samsung. download Running
Samsung. start Play Running Game.
```
## 结论
通过桥接模式,我们可以灵活地组合不同品牌的手机和不同名字的游戏,而不需要为每一种组合编写新的代码。这提供了更好的可维护性和扩展性,允许我们在不修改现有类的情况下添加新的手机品牌和游戏。
优点:
1. 分离抽象接口及其实现部分。使得抽象和实现可以沿着各自的维度来变化,也就是说抽象和实现不再在同一个继承层次结构中,而是“子类化”它们,使它们各自都具有自己的子类,以便任何组合子类,从而获得多维度组合对象。
2. 在很多情况下,桥接模式可以取代多层继承方案,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非常多,桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数。
桥接模式**提高了系统的可扩展性**,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合“开闭原则”
缺点:
1. 桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程。
2. 桥接模式要求正确识别出系统中两个独立变化的维度,因此其**使用范围具有一定的局限性**,如何正确识别两个独立维度也需要一定的经验积累。