• 【设计模式系列】行为型模式之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模式则是对模块或类行为而言的模式,视角不同。





  • 相关阅读:
    Android 图片的缩略图
    Android 非Activity类引用getResources()方法问题的解决方法
    Android 广播(内部类)
    Android 消息广播Intent传递数据
    Android 防止按钮连续点击的方法(Button,ImageButton等)
    Android 广播机制(两种注册方法)与中断广播
    Android 使用意图传递数据
    Android 使用全局变量传递数据
    Android 使用剪切板传递数据
    android 使用静态变量传递数据
  • 原文地址:https://www.cnblogs.com/secbook/p/2655108.html
Copyright © 2020-2023  润新知