【设计模式】 模式PK:策略模式VS状态模式

1、概述

行为类设计模式中,状态模式和策略模式是亲兄弟,两者非常相似,我们先看看两者的通用类图,把两者放在一起比较一下。

策略模式(左)和状态模式(右)的通用类图。

两个类图非常相似,都是通过Context类封装一个具体的行为,都提供了一个封装的方法,是高扩展性的设计模式。但根据两者的定义,我们发现两者的区别还是很明显的:策略模式封装的是不同的算法,算法之间没有交互,以达到算法可以自由切换的目的;而状态模式封装的是不同的状态,以达到状态切换行为随之发生改变的目的。这两种模式虽然都有变换的行为,但是两者的目标却是不同的。我们举例来说明两者的不同点。

人只要生下来就有工作可做,人在孩童时期的主要工作就是玩耍(学习只是在人类具有了精神意识行为后才产生的);成人时期的主要工作是养活自己,然后为社会做贡献;老年时期的主要工作就是享受天伦之乐。按照策略模式来分析,这三种不同的工作方式就是三个不同的具体算法,随着时光的推移工作内容随之更替,这和对一堆数组的冒泡排序、快速排序、插入排序一样,都是一系列的算法;而按照状态模式进行设计,则认为人的状态(孩童、成人、老人)产生了不同的行为结果,这里的行为都相同,都是工作,但是它们的实现方式确实不同,也就是产生的结果不同,看起来就像是类改变了。

2、策略模式实现人生

2.1 类图

这是非常典型的策略模式,没有太多的玄机,它定义了一个工作算法,然后有三个实现类:孩童工作、成年人工作和老年人工作。

2.2 代码

2.2.1 抽象工作算法

class CWorkAlgorithm
{
public:
    CWorkAlgorithm(){};
    ~CWorkAlgorithm(){};
    virtual void mvWork() = 0;
};

2.2.2 工作算法

无论如何,每个算法都必须实现work方法,完成对工作内容的定义,三个具体的工作算法。

//孩童工作
class CChildWork : public CWorkAlgorithm
{
public:
    CChildWork(){};
    ~CChildWork(){};
    void mvWork() { cout << "儿童的工作是玩耍!" << endl; }
};

//成年人工作
class CAdultWork : public CWorkAlgorithm
{
public:
    CAdultWork(){};
    ~CAdultWork(){};
    void mvWork() { cout << "成年人的工作就是先养活自己, 然后为社会做贡献!" << endl; }
};

//老年人工作
class COldWork : public CWorkAlgorithm
{
public:
    COldWork(){};
    ~COldWork(){};
    void mvWork() { cout << "老年人的工作就是享受天伦之乐!" << endl; }
};

2.2.3 环境角色

class CContext
{
public:
    CContext(){};
    ~CContext(){};
    CWorkAlgorithm *mopGetWorkAlgorithm() { return mopWork; }
    void mvSetWorkAlgorithm(CWorkAlgorithm *opWork) { mopWork = opWork; }

    // 每个算法都有必须具有的功能
    void mvWork() { mopWork->mvWork(); }

private:
    CWorkAlgorithm *mopWork;
};

2.2.4 场景调用

int main()
{
    //定义一个环境角色
    CContext *op_context = new CContext;

    cout << "====儿童的主要工作=====" << endl;
    op_context->mvSetWorkAlgorithm(new CChildWork);
    op_context->mvWork();
    cout << "====成年人的主要工作=====" << endl;
    op_context->mvSetWorkAlgorithm(new CAdultWork);
    op_context->mvWork();
    cout << "====老年人的主要工作=====" << endl;
    op_context->mvSetWorkAlgorithm(new COldWork);
    op_context->mvWork();

    return 0;
}

2.2.5 执行结果

在这里我们把每个不同的工作内容作为不同的算法,分别是孩童工作、成年人工作、老年人工作算法,然后在场景类中根据不同的年龄段匹配不同的工作内容,其运行结果如下所示。

2.3 小结

通过采用策略模式我们实现了“工作”这个策略的三种不同算法,算法可以自由切换,到底用哪个算法由调用者(高层模块)决定。策略模式的使用重点是算法的自由切换——老的算法退休,新的算法上台,对模块的整体功能没有非常大的改变,非常灵活。而如果想要增加一个新的算法,比如未出生婴儿的工作,只要继承WorkAlgorithm就可以了。

3、状态模式实现人生

3.1 类图

我们再来看看使用状态模式是如何实现该需求的。随着时间的变化,人的状态变化了,同时引起了人的工作行为改变,完全符合状态模式。这与策略模式非常相似,基本上就是几个类名称的修改而已,但是其中蕴藏的玄机就大了,看看代码你就会明白。

3.2 代码

3.2.1 人的抽象状态

// HumanState.h
class CHuman;

class HumanState
{
public:
    HumanState();
    ~HumanState();

    // 设置一个具体的人
    void mvSetHuman(CHuman *opHuman);
    // 不管人是什么状态都要工作
    virtual  void mvWork() = 0;
protected:
    CHuman *mopHuman;
};

// HumanState.cpp
#include "HumanState.h"
#include "Human.h"

HumanState::HumanState(){}

HumanState::~HumanState(){}

void HumanState::mvSetHuman(CHuman *opHuman)
{
    mopHuman = opHuman;
}

抽象状态定义了一个具体的人(human)必须进行工作(work),但是一个人在哪些状态下完成哪些工作则是由子类来实现的。

3.2.2 孩童状态

// HumanState.h

class CChildState : public HumanState
{
public:
    CChildState();
    ~CChildState();

    void mvWork();
};

// HumanState.cpp

CChildState::CChildState(){}

CChildState::~CChildState(){}

void CChildState::mvWork()
{
    cout << "儿童的工作是玩耍!" << endl;
    mopHuman->mvSetState(new CAdultState);
}

CChildState类代表孩童状态,在该状态下的工作就是玩耍。看着可能有点惊奇,在work方法中为什么要设置下一个状态?因为我们的状态变化都是单方向的,从孩童到成年人,然后到老年人,每个状态转换到其他状态只有一个方向,因此会在这里看到work有两个职责:完成工作逻辑和定义下一状态。

3.2.3 成年人和老年人

//成年人状态
// HumanState.h

class CAdultState : public HumanState
{
public:
    CAdultState();
    ~CAdultState();

    void mvWork();
};

// HumanState.cpp

CAdultState::CAdultState(){}

CAdultState::~CAdultState(){};

void CAdultState::mvWork()
{
    cout << "成年人的工作就是先养活自己, 然后为社会做贡献!" << endl;
    mopHuman->mvSetState(new COldState);
}
//老年人状态
// HumanState.h

class COldState : public HumanState
{
public:
    COldState();
    ~COldState();

    void mvWork();
};

// HumanState.cpp

COldState::COldState(){}

COldState::~COldState(){}

void COldState::mvWork()
{
    cout << "老年人的工作就是享受天伦之乐!" << endl;
}

每一个HumanState的子类都代表了一种状态,虽然实现的方法名work都相同,但是实现的内容却不同,也就是在不同的状态下行为随之改变。

3.2.4 环境角色

//Human.h
class HumanState;

class CHuman
{
public:
    CHuman();
    ~CHuman();

    void mvSetState(HumanState *opHumanState);
    void mvWork();

private:
    HumanState *mopHumanState;
};

//Human.cpp
#include "Human.h"
#include "HumanState.h"

CHuman::CHuman(){}

CHuman::~CHuman(){}

void CHuman::mvSetState(HumanState *opHumanState)
{
    mopHumanState = opHumanState;
    mopHumanState->mvSetHuman(this);
}

void CHuman::mvWork()
{
    mopHumanState->mvWork();
}

定义一个Human类代表人类,也就是状态模式中的环境角色,每个人都会经历从孩童到成年人再到老年人这样一个状态过渡(当然了,老顽童周伯通的情况我们就没有考虑进来),随着状态的改变,行为也改变。

3.2.5 场景调用

int main()
{
    //定义一个普通的人
    CHuman *op_human = new CHuman;
       //设置一个人的初始状态
    op_human->mvSetState(new CChildState);
    cout << "====儿童的主要工作=====" << endl;
    op_human->mvWork();
    cout << "====成年人的主要工作=====" << endl;
    op_human->mvWork();
    cout << "====老年人的主要工作=====" << endl;
    op_human->mvWork();

    return 0;
}

3.2.6 执行结果

3.3 小结

运行结果与策略模式相同,但是两者的分析角度是大相径庭的。策略模式的实现是通过分析每个人的工作方式的不同而得出三个不同的算法逻辑,状态模式则是从人的生长规律来分析,每个状态对应了不同的行为,状态改变后行为也随之改变。从以上示例中我们也可以看出,对于相同的业务需求,有很多种实现方法,问题的重点是业务关注的是什么,是人的生长规律还是工作逻辑?找准了业务的焦点,才能选择一个好的设计模式。

4、总结

从例子中我们可以看出策略模式和状态模式确实非常相似,称之为亲兄弟亦不为过,但是这两者还是存在着非常大的差别,而且也是很容易区分的。

● 环境角色的职责不同

两者都有一个叫做Context环境角色的类,但是两者的区别很大,策略模式的环境角色只是一个委托作用,负责算法的替换;而状态模式的环境角色不仅仅是委托行为,它还具有登记状态变化的功能,与具体的状态类协作,共同完成状态切换行为随之切换的任务。

● 解决问题的重点不同

策略模式旨在解决内部算法如何改变的问题,也就是将内部算法的改变对外界的影响降低到最小,它保证的是算法可以自由地切换;而状态模式旨在解决内在状态的改变而引起行为改变的问题,它的出发点是事物的状态,封装状态而暴露行为,一个对象的状态改变,从外界来看就好像是行为改变。

● 解决问题的方法不同

策略模式只是确保算法可以自由切换,但是什么时候用什么算法它决定不了;而状态模式对外暴露的是行为,状态的变化一般是由环境角色和具体状态共同完成的,也就是说状态模式封装了状态的变化而暴露了不同的行为或行为结果。

● 应用场景不同

两者都能实现前面例子中的场景,但并不表示两者的应用场景相同,这只是为了更好地展示出两者的不同而设计的一个场景。我们来想一下策略模式和状态模式的使用场景有什么不同,策略模式只是一个算法的封装,可以是一个有意义的对象,也可以是一个无意义的逻辑片段,比如MD5加密算法,它是一个有意义的对象吗?不是,它只是我们数学上的一个公式的相关实现,它是一个算法,同时DES算法、RSA算法等都是具体的算法,也就是说它们都是一个抽象算法的具体实现类,从这点来看策略模式是一系列平行的、可相互替换的算法封装后的结果,这就限定了它的应用场景:算法必须是平行的,否则策略模式就封装了一堆垃圾,产生了“坏味道”。状态模式则要求有一系列状态发生变化的场景,它要求的是有状态且有行为的场景,也就是一个对象必须具有二维(状态和行为)描述才能采用状态模式,如果只有状态而没有行为,则状态的变化就失去了意义。

● 复杂度不同

通常策略模式比较简单,这里的简单指的是结构简单,扩展比较容易,而且代码也容易阅读。当然,一个具体的算法也可以写得很复杂,只有具备很高深的数学、物理等知识的人才可以看懂,这也是允许的,我们只是说从设计模式的角度来分析,它是很容易被看懂的。而状态模式则通常比较复杂,因为它要从两个角色看到一个对象状态和行为的改变,也就是说它封装的是变化,要知道变化是无穷尽的,因此相对来说状态模式通常都比较复杂,涉及面很多,虽然也很容易扩展,但是一般不会进行大规模的扩张和修正。

时间: 2024-10-21 05:43:56

【设计模式】 模式PK:策略模式VS状态模式的相关文章

策略模式的孪生兄弟——对状态模式的深度复习总结

俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及的总结知识点如下: 和策略模式的比较 状态模式概念和例子 应用场景 责任链模式和状态模式对比 一种代码优化的思路 java.util.Iterator里也有状态模式的影子 状态模式的优缺点 有限状态机及其应用 前面有总结——策略模式,之前早就觉得策略和状态设计模式有一些相似…… 接口的常用用法都有什么?策略设计模式复习总结 我知道策略模式是对象的行为模式,其实就是对一系列级别平等的算法的封装,它不关心算法实现,让客户端去动态的

设计模式 - 状态模式(state pattern) 未使用状态模式 详解

状态模式(state pattern) 未使用状态模式 详解 本文地址: http://blog.csdn.net/caroline_wendy 状态模式可以控制状态的转换, 未使用设计模式时, 程序会非常繁杂. 具体方法: 1. 状态转换类. /** * @time 2014年7月11日 */ package state; /** * @author C.L.Wang * */ public class GumballMachine { final static int SOLD_OUT =

行为型设计模式之模板方法(TEMPLATE METHOD)模式 ,策略(Strategy )模式

1 模板方法(TEMPLATE METHOD)模式: 模板方法模式把我们不知道具体实现的步聚封装成抽象方法,提供一些按正确顺序调用它们的具体方法(这些具体方法统称为模板方法),这样构成一个抽象基类.子类通过继承这个抽象基类去实现各个步聚的抽象方法,而工作流程却由父类来控制. 2 模板方法应用于下列情况: 1) 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现. 2)各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复.首先识别现有代码中的不同之处,并且将不同之处分离为新

设计模式(二十四)---状态模式

1.简介 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新. 状态模式允许一个对象在其内部状态改变时改变其行为,这个对象看上去就像改变了他的类一样 2.状态模式类图 3.状态模式涉及到的角色 从上图可以看出  状态模式涉及到以下几个角色 3.1.抽象状态(State)角色:定义一个接口,用以封装环境对象的一个特定的状态所对应的行为 3.2.具体状态(ConcreteState)角色:每一个具体状态类都实现了环境的一个状态所对应的行为 3.3.

大量逻辑判断优化的思路——责任链模式复习总结及其和状态模式对比

俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及的总结知识点如下: 责任链模式概念和例子 使用的条件 和状态模式的比较分析 责任链的优缺点 纯的责任链和不纯的责任链 javax.servlet.Filter#doFilter()方法源码分析 基于AOP思想,模拟一个拦截器 前面说了一个状态模式,总结过程中发现和这个责任链的使用场景很类似,都是为了解耦大量复杂业务逻辑判断的,那么他们有什么不同呢?回忆状态模式——状态模式允许通过改变对象的内部状态而改变对象自身的行为,这个对象

设计模式: 自己手动写一个状态模式

状态模式: 允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类.状态模式将状态封装成独立的类,并将动作委托到代表当前状态的对象.状态模式使用组合通过简单引用不同的状态对象来造成类改变的假象. 状态模式和策略模式有相同的类图,但它们的意图不同,策略模式会用行为和算法来配置Context类.状态模式允许Context随着状态的改变而改变其行为. 源代码: </pre><pre name="code" class="java">pac

《大话设计模式》学习笔记12:状态模式

工作状态示例: 1.State: public abstract class State { public abstract void WriteProgram(Work work); } 2.ConcreteState(以ForenoonState.NoonState.SleepingState为例): public class ForenoonState:State { public override void WriteProgram(Work work) { if(work.Hour<1

大话设计模式C++实现-第16章-状态模式

一.UML图 二.概述 下面是来自书本和网络的对状态模式的定义和分析: (1)状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类.看起来,状态模式好像是神通广大很厉害似的--居然能够"修改自身的类"! (2)适用场景:a)状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判 断逻辑简单化.(简单来说,就是把各种if else 转变成了一个个的具体状态,原来if else 每种情况下的

设计模式学习笔记(十七:状态模式)

1.1概述 允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类.这就是状态模式的定义. 一个对象的状态依赖于它的变量的取值情况,对象在不同的运行环境中,可能具有不同的状态.在许多情况下,对象调用方法所产生的行为效果依赖于它当时的状态. 例如,一个温度计(Thermometer)类的实例:温度计类通过调用方法showMessage()显示有关信息时,需要根据当前自己温度(temperature)变量的值来显示有关信息,即根据自己的状态来决定showMessage()方法所体现的

Android设计模式(十三)--状态模式

1.定义: 当一个对象内在状态转变时,允许改变其行为,这个对象看起来改变了其类: 2.优点: 将与特定状态相关的行为局部化,并且将不同状态的行为分割开来: 3.目的: 消除庞大的条件分支语句: 4.使用: 当一个对象的行为取决于它的状态,并且它在运行时刻必须根据状态改变它的行为时,就可以考虑状态模式: 5.描述: 将特定状态的行为都放到一个对象中,将所有与状态相关的代码都放到一个类Status中,通过定义新的子类可以很容易的增加新的状态和转换: 6.缺点: 会增加系统类和对象的个数,增加系统的复