概要 开发中,经常会碰到一些基本逻辑相同,个别算法或处理行为不同的情况,这时如果把所有处理都耦合在一起,会增加模块的复杂度,同时给扩展带来一定难度。一种比较好的方法就是使用Strategy模式来对处理进行解耦,提高扩展性。同时Strategy模式还可以更好的支持"运行时"行为或算法的切换。
目的 对类行为进行解耦,使算法可以相对独立的变化而不至于对Client产生过多的影响。 (每次写概要和目的都比较痛苦,因为这些内容相对比较抽象,自己的文字功底不够,有时要把自己的想法用抽象的文字真正说明清楚还是挺累的)
实例 看这样一个例子吧。设计一个绘图程序,会用到多个第三方绘图方法,首先我们需要能用它来绘制矩形,程序员或许会给出这样的方案:
class Draw { public: void DrawSquare(const Point& s1, const Point& s2, const Point& s3, const Point& s4) { DrawLine(s1, s2); DrawLine(s2, s3); DrawLine(s3, s4); DrawLine(s4, s1); } virtual void DrawLine(const Point& x, const Point& y); }; class DrawLib1 : public Draw { public: virtual void DrawLine(const Point& x, const Point& y) { // drawing line by lib1 from point x to point y } }; class DrawLib2 : public Draw { public: virtual void DrawLine(const Point& x, const Point& y) { // drawing line by lib2 from point x to point y } };
简单说明下上面的代码吧。由于每种绘图库的画线处理不同,所以子类DrawLib1和DrawLib2分别实现了画线函数DrawLine的处理,而在基类Draw中的DrawSqure方法则会利用多态性,基于对象类型使用相应库的DrawLine方法,来绘制矩形。 也许初看来还是个不错的解决方案,但是让我们简单分析一下吧。对于直线型图形的绘制,肯定会有很多不同的需求,比如画三角形,比如波浪线等等,要实现这些需求我们又势必会对类Draw大动干戈,而每种DrawLib都是依赖于类Draw,所以你的每次改动都跟DrawLib1,DrawLib2耦合在一起了,这是我们不希望看到的。 现在可以看出,原来我们把库相关的算法本身和处理逻辑耦合在一起了,那么如果用Strategy模式会怎么解决这个问题呢?
class Draw { public: virtual void DrawLine(const Point& x, const Point& y); }; class DrawLib1 : public Draw { public: virtual void DrawLine(const Point& x, const Point& y) { // drawing line by lib1 from point x to point y } }; class DrawLib2 : public Draw { public: virtual void DrawLine(const Point& x, const Point& y) { // drawing line by lib2 from point x to point y } }; class DrawContext { public: DrawContext(Draw* dr) { mDraw = dr; } void DrawLine(const Point& x, const Point& y) { mDraw->DrawLine(x, y); } voidDrawSquare(const Point& s1, const Point& s2, const Point& s3, const Point& s4) { DrawLine(s1, s2); DrawLine(s2, s3); DrawLine(s3, s4); DrawLine(s4, s1); } private: Draw* mDraw; };
我们把绘图类库相关的算法处理独立出来,而把绘制矩形这些逻辑处理放到类DrawContext里来处理,这样,即使再追加其他逻辑处理也不会影响到类Draw,DrawLib1,DrawLib2。而且我们还可以为DrawContext增加动态切换绘图库的功能,如下所示SetDrawer方法:
class DrawContext { public: DrawContext(Draw* dr) { mDraw = dr; } void SetDrawer(Draw* dr) { mDraw = dr; } void DrawLine(const Point& x, const Point& y) { mDraw->DrawLine(x, y); } voidDrawSquare(const Point& s1, const Point& s2, const Point& s3, const Point& s4) { DrawLine(s1, s2); DrawLine(s2, s3); DrawLine(s3, s4); DrawLine(s4, s1); } private: Draw* mDraw; };
对用户而言,可以很方便的去切换当前使用的绘图库,也就是运行时行为,而不需要去切换绘图对象本身。
Draw* lib1 = new DrawLib1(); Draw* lib2 = new DrawLib2(); DrawContext* draw = new DrawContext(lib1); draw->DrawSquare(); draw.SetDrawer(lib2); draw->DrawSquare(); draw.SetDrawer(lib1); draw->DrawSquare();
另外当对DrawSqure方法又提出不同需求时,还可以考虑从DrawContext继承来实现不同的需求的DrawSquare,同时又不会对类库基本算法本身带来任何影响。
应用 Strategy模式跟Bridge模式类结构很相似,但是Bridge模式是针对结构而言,而Strategy模式则是对模块或类行为而言的模式,视角不同。