zoukankan      html  css  js  c++  java
  • 设计模式之状态模式

    2018-09-22 16:16:13

      要避免过长的方法,面向对象设计实际上是希望做到代码的责任分解。

    状态模式

      状态(State)模式,当一个对象的内在状态改变时允许改变其行为,这个对象看起来就像是改变了其类。状态模式主要解决的是当控制一个对象状态转换条件表示式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。当然若果这个判断条件很简单,那么就没有必要用状态模式了。

    状态模式UML类图

      State:抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为。

      ConcreteState:具体状态,每一个子类实现一个与Context的一个状态相关的行为。要注意具体状态和状态之间还有可能会有状态的变迁。

      Context:维护一个ConcreteState子类的实例,这个实例定义当前的状态。

    状态模式的好处

    优点:

      1.将与特定状态相关的行为局部化,并且将不同的状态分隔开来,将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类就可以很容易的增加新的状态和转换。这样做能消除庞大的条件分支语句,大的分支判断会使得它们难以修改和扩展,装填模式通过把各种状态转移逻辑分别到State的子类之间,来减少相互之间的依赖。

      2.在状态模式中,对象的行为是其状态中函数的结果,并在运行时根据状态改变行为,这就消除了对ifelse/switchcase的依赖。

    缺点:

      1.使用状态模式必然会增加工程中类的数量。

      2.由于状态模式的实现与结构较为负责,如果使用不当很容易出错。

      3.当增加新的状态时,则需要修改负责转换的源代码,否则不能转换到新增的状态。

    适用场景:

      1.当一个对象的行为取决于它的状态(也就是说存在大量if/else 或者switch/case的时候),并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式。

      2.如果业务需求某项业务有多个状态,通常都是一些枚举常量,状态的变化都是依靠大量的分支判断语句来实现,此时应该考虑将每一种业务状态定义为一个State的子类,这样这些对象就可以不依赖于其它对象而独立变化了。

    代码示例

      场景:马上期末考试了,听说不及格会……小于60,不及格:把填空写错的都给我回去抄三遍!大于等于60小于70,:不行呀,还是需要加油,课文抄两遍吧。大于等于70小于85,还不错,抄一遍就可以了。85以上,哎哟,这次考试这么难,不错呀,去玩吧。

    1.抽象状态类(State):这个类它要做什么呢,其实,就是根据外部状态的不同来做出不同的行为,利用了多态的特性,State中有一个Handle接口用于根据状态的不同来做出不同的反应(改变状态)

    #ifndef STATE_H_
    #define STATE_H_
    class Context;
    
    class State
    {
    public:
        virtual void handle(Context objGetCondition) = 0;
        State() = default;
        virtual ~State() = default;
    };
    #endif
    Abstract state

    2.具体状态类(这个类值得好好看一下。用不好就真的是灾难)

    #ifndef LESSTHAN60STATE_H_
    #define LESSTHAN60STATE_H_
    #include "State.h"
    #include "Between60And70State.h"
    #include <iostream>
    class LessThan60State : public State
    {
    public:
        void handle(Context objGetState) override;
        LessThan60State() = default;
        ~LessThan60State()
        {
        if(nullptr != m_Between60And70State)
            {
            delete m_Between60And70State;
            m_Between60And70State = nullptr;
            }
        }
    private:
        Between60And70State *m_Between60And70State{nullptr};
    };
    #endif
    
    #include "LessThan60State.h"
    #include "Context.h"
    
    void LessThan60State::handle(Context objGetState)
    {
        if(objGetState.getCondition() < 60 )
        {
        std::cout << "Your socre is lower than 60.You need copy this text 3 times." << std::endl;
        }
        else
        {
        //exceed 60 ,turn up to state 70,不能中文注释真的很痛苦
        //Between60And70State objNewState;
            m_Between60And70State = new Between60And70State;
            objGetState.setState(m_Between60And70State); 
            objGetState.request();
        }
    }
    
    #ifndef BETWEEN60AND70STATE_H_
    #define BETWEEN60AND70STATE_H_
    #include "State.h"
    #include <iostream>
    class Between60And70State : public State
    {
    public:
        void handle(Context objGetState) override;
        Between60And70State() = default;
        ~Between60And70State() = default;
    };
    #endif 
    
    #include "Between60And70State.h"
    #include "Context.h"
    #include "Between70And85State.h"
    
    void Between60And70State::handle(Context objGetState)
    {
        int iSocre = objGetState.getCondition();
        if(iSocre >= 60 && iSocre < 70)
        {
        std::cout << "your socre is "<< iSocre <<".You need to copy this 2 times." << std::endl;
        }
        else
        {
        //exceed 70 or lower than 60.Suppose that condition is all right( exceed than 70 )
        //大于70分的时候要进行一次状态的跃迁
        Between70And85State objNewState;
            objGetState.setState(&objNewState);
        objGetState.request();
        }
    }
    
    #ifndef BETWEEN70AND85STATE_H_
    #define BETWEEN70AND85STATE_H_
    #include "State.h"
    #include "Between85And100State.h"
    #include <iostream>
    class Between70And85State : public State
    {
    public:
        void handle(Context objGetState) override;
        Between70And85State() = default;
        ~Between70And85State() = default;
    private:
        Between85And100State *m_Between85And100State{nullptr};
    };
    #endif
    
    #include "Between70And85State.h"
    #include "Context.h"
    
    void Between70And85State::handle(Context objGetState)
    {
        int iSocre = objGetState.getCondition();
        if(iSocre >=70 && iSocre < 85)
        {
        std::cout << "Good job.But you need copy this text 1 time." << std::endl;
        }
        else
        {
        //大于85分,跃迁到下一个状态
        //Between85And100State objNewState;
            m_Between85And100State = new Between85And100State;
        objGetState.setState(m_Between85And100State);
            objGetState.request();
        }
    }
    
    #ifndef BETWEEN85AND100STATE_H_
    #define BETWEEN85AND100STATE_H_
    
    #include "State.h"
    #include "LessThan60State.h"
    #include <iostream>
    
    class Between85And100State : public State
    {
    public:
        void handle(Context objGetState) override;
        Between85And100State() = default;
        ~Between85And100State()
        {
        if(nullptr != m_LessThan60State )
            {
            delete m_LessThan60State;
                m_LessThan60State = nullptr;
             }
        } 
    private:
        LessThan60State* m_LessThan60State{nullptr};
    };
    #endif
    
    #include "Between85And100State.h"
    #include "Context.h"
    
    void Between85And100State::handle(Context objGetCondition)
    {
        int iSocre = objGetCondition.getCondition();
        if(iSocre > 85 && iSocre <= 100 )
        {
        std::cout << "Very good!Just playing." << std::endl;
        }
        else
        {
        //如果不满足条件,那么就从最第一级的条件开始,为了保证程序不出错误,在Context的setCondition方法里设置条件的时候必须要对条件做一个符合这个系统的检查
        //否则模式将会陷入死循环
        m_LessThan60State = new LessThan60State;
        //LessThan60State newState;
            objGetCondition.setState(m_LessThan60State);
            objGetCondition.request();
        }
    }
    Concrete State

    3.上下文类(Context)

    ifndef CONTEXT_H_
    #define CONTEXT_H_
    #include "State.h"
    #include "Between85And100State.h"
    #include <iostream>
    class Context
    {
    public:
        bool setCondition(const int iCondition)
        {
            if(iCondition < 0 || iCondition >100)
            {
            std::cout << "The input is error.Socre must between 0 and 100." << std::endl;
                return false;
            }
        m_iCondition = iCondition;
            return true;
        }
        int getCondition() const
        {
        return m_iCondition;
        }
        void setState(State* objState)
        {
        m_currentState = objState;
        }
        void setIniState(State* objState)
        {
        m_currentState = objState;
        }
        void request()
        {
            if(nullptr == m_currentState)
            {
             std::cout << "nullptr,you must call set initState first. " << std::endl;
                 return;
            }
        m_currentState->handle(*const_cast<Context *>(this));
        }
        Context()
        {
        //初始状态设置为85-100这个区间段,大家都是好学生嘛
        //m_initState = new Between85And100State;
            // m_currentState = m_initState;
        }
        ~Context()
        {
        /*if(nullptr != m_initState)
            {
                   delete m_initState;
                m_initState = nullptr;
           }*/
        }
    private:
        int m_iCondition{0};
        State *m_currentState{nullptr};
        State *m_initState{nullptr};
    };
    #endif
    Context

    4.Client

    #include "Context.h"
    #include "Between85And100State.h"
    using namespace std;
    
    int main(int argc,char *argv[])
    {
        Context objContext;
        bool bRet = objContext.setCondition(75);
        Between85And100State  obj100State;
        objContext.setIniState(&obj100State);
        if(false == bRet)
        return (0);
      
        objContext.request();    
        return(1);
    }
    Client
  • 相关阅读:
    MapInfo 文件解析
    XML 序列化与反序列化
    GPS定位RTK解决方案
    JS遍历OCX方法
    Oracle 11g的日志路径
    临时表空间
    Oracle Stream 同步数据
    通过merge语句完成表数据同步
    处理机调度
    特征选取方法PCA与LDA
  • 原文地址:https://www.cnblogs.com/ToBeExpert/p/9691510.html
Copyright © 2011-2022 走看看