diff --git a/08_bridge.cpp b/08_bridge.cpp new file mode 100644 index 0000000..3eac539 --- /dev/null +++ b/08_bridge.cpp @@ -0,0 +1,86 @@ +#include +#include + +using namespace std; + +class Texture { +public: + Texture() = default; + virtual ~Texture() = default; + + virtual const string& show_texture() = 0; +}; + +class Shape { +protected: + shared_ptr t; +public: + Shape(shared_ptr t) : t(t) {} + virtual void draw() = 0; +}; + + +class Square : public Shape { + int x1; + int y1; + int x2; + int y2; +public: + Square(shared_ptr t, int x1, int y1, int x2, int y2) : + Shape(t), x1(x1), y1(y1), x2(x2), y2(y2) {} + + virtual void draw() { + // Maybe you have to use different show_texture. For example, + // show_texture_square for Square, show_texture_circle for Circle, + // so that the actions is M*N, but that is unavoidable, and the + // class's count is M+N. + cout << "draw square: " << x1 << " " << y1 << " " << x2 << " " << y2 + << " with texture " << t->show_texture() << endl; + } +}; + +class Circle : public Shape { + int x; + int y; + int r; +public: + Circle(shared_ptr t, int x, int y, int r) : + Shape(t), x(x), y(y), r(r) {} + + virtual void draw() override { + cout << "draw circle: " << x << " " << y << " " << r << " with texture " + << t->show_texture() << endl; + } +}; + +class SoilTexture : public Texture { + string s; +public: + SoilTexture(int level) : s("Soil") { s += to_string(level); } + virtual const string& show_texture() override { + return s; + } +}; + +class WaterTexture : public Texture { + string s; +public: + WaterTexture(int level) : s("Water") { s += to_string(level + 256); } + virtual const string& show_texture() override { + return s; + } +}; + +int main() { + shared_ptr ts = make_shared(123); + shared_ptr tw = make_shared(11); + // When you want to create a new texture, you just need to create one + // texture class and use it when generating shape. + // you don't need to create a set of shapes. + // You can switch different Textures esaily when running. + + auto c = make_shared(ts, 1, 2, 3); + auto s = make_shared(tw, 1, 2, 3, 4); + c->draw(); + s->draw(); +} diff --git a/notes.md b/notes.md index 74ebb63..1bdc23b 100644 --- a/notes.md +++ b/notes.md @@ -49,3 +49,11 @@ 适配器类可以继承自 `A`,并将 `B` 作为构造参数。在构造过程中,适配器将 `B` 的接口转换为符合 `A` 的接口形式。这样,通过适配器,`B` 就可以像 `A` 一样调用接口,达到接口兼容的效果。考虑到适配性,比如希望这个适配器类还能适配其他的 `C`,`D` 等,可以让适配器类接受一个更普遍的共性作为构造参数。 需要注意的是,这种做法显然会使得代码复杂很多,条件允许的话,还是直接修改 `B` 类较好。 + +## 桥接模式(Bridge) + +有时类有多个变化维度需要组合。以图形为例,它既有“形状”的维度,也有“纹理”的维度。若采用继承来组合这两个维度,将导致类数量呈乘法增长(`M * N`)。而且,一个维度的变更都可能影响到多个类。 + +桥接模式给出的解决方案是:“组合优于继承”。它通过将“形状”和“纹理”分别建模为独立的类层次结构,并在“形状”中组合一个“纹理”的引用,使两者的组合关系从原来的 `M * N` 转变为 `M + N`。这样可以自由扩展任意一侧,而无需影响另一侧。即使实现中可能仍需要 `M * N` 种行为(这是无法减少的),但是当一个纹理要改变时,也只需要改这个纹理类而不影响其他类。此外,这种组合关系是运行时决定的,因此可以动态地将不同的抽象与实现配对,增强了系统的灵活性。 + +在桥接模式中,抽象部分(也称为接口)定义的是高层的控制逻辑,而真正的工作则由被称为实现部分的组件完成。抽象层在调用时,会将具体任务委派给实现层,从而实现灵活的组合与运行时绑定。