设计模式(一) 策略模式

 一  入门

摘自<Head First Design Patterns> chapter 1

1  飞翔的鸭子

开发一款模拟鸭子的游戏。首先设计一个鸭子母类,里面有鸭子的叫声、游泳和外形三个成员函数,然后在野鸭和红头鸭两个子类中重写继承的外形函数。

更进一步,要求鸭子会飞,应该如何设计程序呢?

2  两种方法

1)  继承

为 Duck 母类添加 fly() 成员函数,然后各个子类继承 fly()

问题: 并不是所有的鸭子都会飞,比如“煮熟的鸭子”。解决: 不会飞的鸭子自母类继承 fly() 后,重写该函数。

因为鸭子会不会飞,又发现一个bug,并不是所有的鸭子都会叫,比如“大黄鸭”。因此,不同的子类也需要重写 quack()

此时问题又来了,由于开发的是游戏软件,要求游戏每三个月更新一次,每次更新后鸭子的飞行方式和叫声都可能发生新的变化。那么,岂不是每三个月都要重写 RubberDuck 和 DecoyDuck 子类甚至更多子类的 fly() 和 quack() 函数,有点繁琐。

于是,有了第二个方法...

2)  接口 (interface)

视会飞和会叫为一种能力,并将 Flyable 和 Quackable 做成接口 (interface),然后在里面加上相应的函数 fly() 和 quack()

这样,只有会飞的子类实现该接口 Flyable, 并且具有 fly() 函数。同理只有会叫的子类实现接口 Quackable 且有 quack() 函数。

问题又来了,飞行是鸭子的一种行为,不同的鸭子,其各自飞行的方式也不同。显然,每个子类中还得重写 fly() 和 quack()。因此,单纯的使用接口或继承都非最佳。

那么,到底什么方法能完美解决鸭子 fly 和 quack 的问题呢?不着急,先看下面的三个设计原则。

3  三个原则

1)  identify what varies and separate them from what stays the same

鸭子类中, 很明显“飞”和“叫”是变化的,于是先把 fly 和 quack 择出来,和其它不变的分隔开来。

2)  program to an interface, not an implementation

3)  favor composition over inheritance

HAS-A better than IS-A

Java: Duck 母类中定义 FlyBehavior 和 QuackBehavior 的成员对象 (FlyBehavior flyBehavior)

C++: Duck 母类中定义 FlyBehavior 和 QuackBehavior 的指针 (FlyBehavior * pflyBehavior)

4  一个模式

  defines a family of algorithms,  encapsulates each one,  and makes them interchangeable.

    (Strategy lets the algorithm vary independently from clients that use it)

1)  client

2)  encapsulated fly behavior

把飞行封装为接口,里面具体实现不同的飞行方式,即将各种”飞行方式“视为一系列”算法“,添加或者修改算法都在这个接口里面进行。

这样,不同的鸭子子类,只需通过母类中的成员对象 (flyBehavior) 调用相应的”飞行算法“即可。

3)  encapsulated quack behavior

Java: 直接使用关键字 interface 便可将 QuackBehavior 定义成接口

C++: 接口 ≈ 抽象基类,将 QuackBehavior 定义为抽象基类,也即声明 quack() 为纯虚函数, 具体代码形式为 “void quack() = 0”

 二  进阶

摘自<Design Paterns_Elements of Reusable Object-Oriented Software>

上面偏重于入门,下面开始进阶,偏重于策略模式的适用情景。

1  Intent

Define a family of algorithms, encapsulate each one, and make them interchangeable.

Strategy lets the algorithm vary independently from clients that use it.

2  Applicability

1)  many related classes differ only in their behavior

  = strategies provide a way to configure a class with one of many behaviors

2)  you need different variants of an algorithm

  = strategies can be used when these variants are implemented as a class hierarchy of algorithms

3)  an algorithm uses data that clients should not know about

  = use strategies to avoid exposing complex, algorithm-specific data structures

4)  a class defines many behaviors, and these appear as multiple conditional statements in its operations

  = move related conditional branches into their own strategy class

3  Sample

many algorithms exist for breaking a stream of text into lines, and
hard-wiring such algorithms into the class that require them is not
desirable.

C++ 实例:

1) class Composition

/* Composition class maintains a collection of Component instances */
class Composition {
public:
    Composition(Compositor*);
void Repair();
private:
    Compositor* _compositor;
    Component* _components;    // the list of components
    int _componentCount;    // the number of components
    int _lineWidth;        // the Composition‘s line width
    int* _lineBreaks;    // the position of linebreaks in components
    int _lineCount;        // the number of lines
};

void Composition::Repair () {
    Coord* natural;
    Coord* stretchability;
    Coord* shrinkability;
    int componentCount;
    int* breaks;
    // prepare the arrays with the desired component sizes
    // ...
    // determine where the breaks are:
    int breakCount;
    breakCount = _compositor->Compose(
        natural, stretchability, shrinkability,
        componentCount, _lineWidth, breaks
    );
    // lay out components according to breaks
    // ...
}

2) class Compositor and its subclasses

/* Compositor is an abstract class(also interface) */
class Compositor{
public:
    virtual int Compose(
        Coord natural[], Coord stretch[],Coord shrink[],
        int componentCount, int lineWidth, int breaks[]
    ) = 0;
protected:
    Compositor();
};

/* Three subclasses */
class SimpleCompositor : public Compositor {
public:
    SimpleCompositor();

    virtual int Compose(
        Coord natural[], Coord stretch[], Coord shrink[],
        int componentCount, int lineWidth, int breaks[]
    );
    // ...
};

class TeXCompositor : public Compositor {
public:
    TeXCompositor();
    virtual int Compose(
        Coord natural[], Coord stretch[], Coord shrink[],
        int componentCount, int lineWidth, int breaks[]
    );
    // ...
};

class ArrayCompositor : public Compositor{
public:
    ArrayCompositor(int interval);

    virtual int Compose(
        Coord natural[], Coord stretch[], Coord shrink[],
        int componentCount, int lineWidth, int breaks[]
    );
    // ...
};

3) instantiation

Composition* quick = new Composition(new SimpleCompositor);
Composition* slick = new Composition(new TeXCompositor);
Composition* iconic = new Composition(new ArrayCompositor(100));

时间: 2024-10-17 21:47:33

设计模式(一) 策略模式的相关文章

设计模式之策略模式(Strategy)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

Head First 设计模式之一 策略模式

策略模式 定义 策略模式定义了算法族,分别封装起来,让他们之间可以相互转换,此模式让算法的变化独立于使用算法的客户. 实例 上面的定义看起来说的不太清楚,记定义无意义,理解策略模式还是要看书中的鸭子例子.假设设计一个模拟鸭子的游戏,鸭子的种类有很多,有红头鸭.绿头鸭等等,鸭子可以划水,可以呱嘎叫.在这个模拟游戏的实现上,自然会想到用继承的方法,定义一个鸭子基类,具体的鸭子类型继承自鸭子基类.如下图所示 所有鸭子都会飞.呱呱叫和游泳,这些功能由基类来实现,display函数用来输出鸭子实例的类型,

JavaScript设计模式之策略模式(学习笔记)

在网上搜索“为什么MVC不是一种设计模式呢?”其中有解答:MVC其实是三个经典设计模式的演变:观察者模式(Observer).策略模式(Strategy).组合模式(Composite).所以我今天选择学习策略模式. 策略模式:定义了一系列家族算法,并对每一种算法单独封装起来,让算法之间可以相互替换,独立于使用算法的客户. 通常我并不会记得“牛顿第一定律”的具体内容,所以我也难保证我会对这个定义记得多久……用FE经常见到的东西来举个例子说明一下: $("div").animation(

大话设计模式_策略模式(Java代码)

策略模式:定义算法家族,分别封装,让它们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户 简单描述:一个父类,多个子类实现具体方法.一个Context类持有父类的引用(使用子类实例化此引用),客户端代码只需要与此Context类交互即可 大话设计模式中的截图: 例子代码: 策略类: 1 package com.longsheng.strategy; 2 3 public abstract class Strategy { 4 5 public abstract double getR

设计模式之策略模式20170720

行为型设计模式之策略模式: 一.含义 策略模式是一种比较简单的模式,也叫做政策模式,其定义如下: 定义一组算法(可抽象出接口),将每个算法都封装起来,并且使它们之间可以互换(定义一个类实现封装与算法切换) 二.代码说明 1.主要有两个角色 1)Context封装角色 它也叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略,算法的直接访问,封装可能存在的变化. 2)策略角色 该类含有具体的算法 2.在用C实现过程中也是参考这种思想,以压缩,解压算法举例,具体实现如下: 1)策略模式使用场景

&lt;二&gt;读&lt;&lt;大话设计模式&gt;&gt;之策略模式

又和大家见面了,能够坚持写出第二篇文章真不错,好好加油. <<大话设计模式>>讲解策略模式是以商场收银软件程序开头的,那么问题来了,哪家商场收银软件强,开玩笑了.读过上篇文章<<简单工厂模式>>的知道,它有两个缺点:1.客户端依赖两个类,耦合性高:2.如果算法过多则需要写很多类.解决上面问题的就是策略模式了. 策略模式:它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户. 商场收银软件:单价*打折算法=售价.

设计模式之策略模式C++实现

策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户. 策略模式UML图如下: 举例: 游泳池中有不同种类的鸭子,有绿头鸭,红头鸭,橡皮鸭,木头鸭等.不同鸭子的特征或行为不同.绿头鸭(MallardDuck)可以叫声是"quack",会飞:橡皮鸭叫声是"queak",不会飞:木头鸭不会叫,也不会飞.利用面向对象原理来设计来泳池中的各种鸭.要求:1.可扩展性好,当有新鸭加入时或鸭的行为有变动时,不用大量改动代码:2.复用性

如何让孩子爱上设计模式 ——14.策略模式(Strategy Pattern)

如何让孩子爱上设计模式 --14.策略模式(Strategy Pattern) 描述性文字 本节讲解的是行为型设计模式中的第一个模式: 策略模式, 这个模式非常简单,也很好理解. 定义一系列的算法,把每个算法封装起来,并使得他们可以相互替换, 让算法独立于使用它的客户而变化. 一般用来替换if-else,个人感觉是面向过程与面向对象思想的 过渡,这里举个简易计算器的栗子,帮助理解~ 普通的if-else/switch计算器 普通的面向过程if-else简易计算器代码如下: 运行结果如下: 这里我

[design-patterns]设计模式之一策略模式

设计模式 从今天开始开启设计模式专栏,我会系统的分析和总结每一个设计模式以及应用场景.那么首先,什么是设计模式呢,作为一个软件开发人员,程序人人都会写,但是写出一款逻辑清晰,扩展性强,可维护的程序就不是那么容易做到了.现实世界的问题复杂多样,如何将显示问题映射到我们编写的程序中本就是困难重重.另一方面,软件开发中一个不变的真理就是"一切都在变化之中",这种变化可能来自于程序本身的复杂度,也可能来自于客户不断变化的需求,这就要求我们在编写程序中一定要考虑变化的因素,将变化的因素抽离出来,

【设计模式】策略模式(经过改进的)

和前文讲到的经过改进的简单工厂模式类似,这里把策略模式和简单工厂模式结合起来. 改进的关键是改写[TodoContext]上下文类,要在该类中产生对象([干活]类的动态类型),对外提供的干活函数不变. 如下: 最大的不同就是CTodoContext类的构造函数参数不同了,构造函数成为类似简单工厂模式类的参数, 构造函数根据此参数去实例化不同的子类 //[ToDo上下文]类 class CTodoContext { public: //构造函数的参数为一个[活的名字] //构造函数根据这个名字去实