【设计模式系列】行为型模式之Strategy模式

概要
开发中,经常会碰到一些基本逻辑相同,个别算法或处理行为不同的情况,这时如果把所有处理都耦合在一起,会增加模块的复杂度,同时给扩展带来一定难度。一种比较好的方法就是使用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模式则是对模块或类行为而言的模式,视角不同。





posted @ 2012-05-14 22:39  MXi4oyu  阅读(200)  评论(0编辑  收藏  举报