由浅入深,让你全面了解状态机模式的应用原理

工作中应用到了状态机,学习过程中发现,如果状态机使用得当,那么就会事半功倍。中间也陆陆续续学习研究了状态机的相关知识。所以,在这里做个总结,同时也分享出来。

本文首先简单介绍状态机的基本知识(建议找专门专业的介绍状态机的书籍进行学习),然后基于十字转门的例子,以迁移表的方式来实现有限状态机的功能,接着再介绍经典的状态机模式,最后重点介绍boost startchart的相关知识点,boost startchart是boost实现的状态机库,它几乎支持了所有的UML中状态机的特性,主要学习的途径就是官网提供的指南,该指南信息量很大,但是学习起来有点费劲,而且例子也不够完整,所以,本文也会基于它提供的例子,比如hello world、秒表、数码相机,重新梳理总结它的应用方式,至于高级议题,可能需要再花时间进行研究。

一、状态机基本知识

一般状态机由三个元素组成:状态、事件、反应。而反应在boost startchart包括转移、动作等。一个状态可以对应一个或者多个反应。

当前状态收到事件后,执行反应,然后转变为新的状态。该流程会使用下图的方式来表示。

状态机通常都需要有历史状态,可以用来恢复,它分为浅历史和深历史两类。

历史状态是伪状态, 其目的是记住从组合状态中退出时所处的子状态, 当再次进入组合状态时, 可以直接进入这个子状态, 而不是再从组合状态的初态开始。

浅历史状态, 只记住最外层组合状态的历史,使用大写H来表示。
深历史状态, 可以记住任意深度的组合状态的历史,使用大写H和星号组合来表示。

二、迁移表

进出地铁的时候,有时候设置的是一个十字转门,十字转门默认是锁的状态,当投入硬币之后,当前十字转门就会变成解锁状态,当人通过之后,十字转门又会变成锁的状态。当十字转门是锁的状态,但是强行通过,就会发出警告信息。其状态的转换如下图所示。

接下来,我们通过迁移表的方式来说实现上图的状态机图。

首先定义实现动作类接口和实现,即unlock/lock/alarm/thanks。这里定义十字转门的控制接口JTurnstileControlInterface,主要是依据开闭原则,当动作类的功能改变的时候,只需要继承JTurnstileControlInterface接口类,然后重新实现对应的接口函数。

/// 十字转门控制接口
class JTurnstileControlInterface
{
public:
    virtual void lock() = 0;
    virtual void unlock() = 0;
    virtual void thanks() = 0;
    virtual void alarm() = 0;
};

/// 十字转门控制类
class JTurnstileControler : public JTurnstileControlInterface
{
public:
    virtual void lock() override;
    virtual void unlock() override;
    virtual void thanks() override;
    virtual void alarm() override;
};

/// 十字转门控制类的实现
void JTurnstileControler::lock()
{
    LOG(INFO) << "action lock";
}

void JTurnstileControler::unlock()
{
    LOG(INFO) << "action unlock";
}

void JTurnstileControler::thanks()
{
    LOG(INFO) << "action thanks";
}

void JTurnstileControler::alarm()
{
    LOG(INFO) << "action alarm";
}

然后定义状态和事件,LOCKED和UNLOCKED表示的是十字转门的状态,COIN和PASS表示的是十字转门收到的事件。

// 状态
static const int LOCKED = 0;
static const int UNLOCKED = 1;

// 事件
static const int COIN = 0;
static const int PASS = 1;

有了上面的基础之后,最后就可以以迁移表的方式来实现十字转门的状态机图。

定义十字转门类,构造函数接受动作类,event接收事件,Transition是存储状态转移关系的内部类。

class Turnstile
{
public:
     // 传入动作
     Turnstile(JTurnstileControlInterface* pControler);

     // 接受事件
     void event(int event);

private:
    typedef std::function<void ()> Action;

    class Transition
    {
    public:
        Transition(int curState, int event, int newState, Action action)
            : m_curState(curState)
            , m_event(event)
            , m_newState(newState)
            , m_action(action)
        {}

        int m_curState;
        int m_event;
        int m_newState;
        Action m_action;
    };

    // 添加迁移关系到vector向量
    void AddTransition(int curState, int event, int newState, Action action);

private:
    JTurnstileControlInterface* m_pTurnstileConstroler;
    std::vector<Transition*> m_vecTransition;
    int m_iState;
};

实现十字转门类,event是处理接收到事件的函数,该函数会遍历vector向量中存储的状态迁移表,如果匹配到对应的事件,那么修改当前的状态,并且执行对应的动作。

Turnstile::Turnstile(JTurnstileControlInterface* pControler)
    : m_iState(LOCKED)
{
    m_pTurnstileConstroler =  pControler;
    AddTransition(LOCKED, COIN, UNLOCKED, std::bind(&JTurnstileControlInterface::unlock, m_pTurnstileConstroler));
    AddTransition(LOCKED, PASS, LOCKED, std::bind(&JTurnstileControlInterface::alarm, m_pTurnstileConstroler));
    AddTransition(UNLOCKED, COIN, UNLOCKED, std::bind(&JTurnstileControlInterface::thanks, m_pTurnstileConstroler));
    AddTransition(UNLOCKED, PASS, LOCKED, std::bind(&JTurnstileControlInterface::lock, m_pTurnstileConstroler));
}

void Turnstile::AddTransition(int curState, int event, int newState, Action action)
{
    m_vecTransition.push_back(new Transition(curState, event, newState, action));
}

void Turnstile::event(int event)
{
    for(unsigned int i = 0; i < m_vecTransition.size(); i++)
    {
        Transition* p_trans = m_vecTransition.at(i);
        if (m_iState == p_trans->m_curState && event == p_trans->m_event)
        {
            m_iState = p_trans->m_newState;
            p_trans->m_action();
            break;
        }
    }
}

实现完成十字转门之后,现在就来验证下效果,首先创建十字转门的动作对象指针,将其传入十字转门对象的构造函数,然后调用event函数,传入事件COIN, 执行完成之后,再传入事件PASS,来查看当前动作的执行是否正确。

JTurnstileControler* p_controler = new JTurnstileControler();
Turnstile turnstile(p_controler);

turnstile.event(COIN);
turnstile.event(PASS);

最后运行程序,输出的信息如下,从中可以看到,接收到COIN事件,执行了unlock动作,接收到PASS事件,执行了lock动作,这个符合预期。

[virtual void JTurnstileControler::unlock():36] action unlock
[virtual void JTurnstileControler::lock():31] action lock

三、状态机模式

上面通过迁移表的方式来实现状态机图,接下来就来介绍状态机模式,该设计模式也是比较经典的。它将状态逻辑与动作解耦,context是上下文对象,它主要实现动作功能,该状态机的模式的主要关键点是,状态对象持有context上下文对象的指针。

定义状态基类、状态A和状态B

class Context;
/// 状态基类
class State
{
public:
    State();
    virtual ~State();
    virtual void OperationInterface(Context* ) = 0;
    virtual void OperationChangeState(Context* ) = 0;
protected:
    bool ChangeState(Context* con,State* st);
};

/// 状态对象A
class SubStateA : public State
{
public:
    SubStateA();
    virtual ~SubStateA();
    virtual void OperationInterface(Context* );
    virtual void OperationChangeState(Context*);
protected:
private:
};

/// 状态对象B
class SubStateB : public State
{
public:
    SubStateB();
    virtual ~SubStateB();
    virtual void OperationInterface(Context* );
    virtual void OperationChangeState(Context*);
protected:
private:
};

接着实现定义状态基类、状态A和状态B

///
/// State
///
State::State()
{
}

State::~State()
{
}

void State::OperationInterface(Context*)
{
}

void State::OperationChangeState(Context*)
{

}

bool State::ChangeState(Context* con,State* st)
{
    con->ChangeState(st);
    return true;
}


///
/// SubStateA
///
SubStateA::SubStateA()
{

}

SubStateA::~SubStateA()
{

}
void SubStateA::OperationInterface(Context* )
{
    LOG(INFO) << "SubStateA::OperationInterface";
}

void SubStateA::OperationChangeState(Context* con)
{
    OperationInterface(con);
    this->ChangeState(con,new SubStateB());
}


///
/// SubStateB
///
SubStateB::SubStateB()
{

}

SubStateB::~SubStateB()
{

}
void SubStateB::OperationInterface(Context* )
{
    LOG(INFO) << "SubStateB::OperationInterface";
}

void SubStateB::OperationChangeState(Context* con)
{
    OperationInterface(con);
    this->ChangeState(con,new SubStateA());
}

最后关键是定义上下文对象context,  其中声明State为Context的友元类,这表明在State类中可以访问 Context 类的 private 字段。

class Context
{
public:
    Context();
    Context(State* state);
    ~Context();
    void OprationInterface();
    void OperationChangState();
protected:

private:
    friend class State; //表明在State类中可 以访问 Context 类的 private 字段
    bool ChangeState(State* state);

private:
    State* m_pState;
};

实现上下文对象context

///
/// Context
///
Context::Context()
{
}

Context::Context(State* state)
{
    this->m_pState = state;
}

Context::~Context()
{
    delete m_pState;
}

void Context::OprationInterface()
{
    m_pState->OperationInterface(this);
}

bool Context::ChangeState(State* state)
{
    this->m_pState = state;
    return true;
}

void Context::OperationChangState()
{
    m_pState->OperationChangeState(this);
}

实现完成所有状态机相关的代码之后,现在就来验证下状态的转移效果。

State* p_state_a = new SubStateA();
Context* p_context = new Context(p_state_a);

p_context->OperationChangState();
p_context->OperationChangState();
p_context->OperationChangState();

if (p_context != nullptr)
{
    delete p_context;
    p_context = nullptr;
}

if (p_state_a != nullptr)
{
    p_state_a = nullptr;
}

运行输出的打印信息如下,状态的转移从A到B,再到A。

[virtual void SubStateA::OperationInterface(Context *):71] SubStateA::OperationInterface
[virtual void SubStateB::OperationInterface(Context *):95] SubStateB::OperationInterface
[virtual void SubStateA::OperationInterface(Context *):71] SubStateA::OperationInterface

四、boost startchart

boost startchart是boost实现的状态机库,它几乎支持了所有的UML中状态机的特性。

首先来看下一个简单的实现来初步了解其使用方法和机制。boost::statechart的状态机,它大量了引用了CRTP,  基本思想要点是:派生类要作为基类的模版参数。更详细的原理可以参考《学会了这么神奇的模版模式,让你C++模版编程之路事半功倍》。首先需要实现继承state_machine的类Machine,其初始状态为Greeting。然后再实现继承simple_state的状态Greeting。

class Greeting;
class Machine : public boost::statechart::state_machine< Machine, Greeting >
{
public:
    void Print() { LOG(INFO) << "xxx"; }
};
class Greeting : public boost::statechart::simple_state< Greeting, Machine >
{
public:
    Greeting() { LOG(INFO) << "Hello World!"; }     // entry 進入
    ~Greeting() { LOG(INFO) << "Bye Bye World!"; }  // exit 退出
};

然后看下如何启动和使用上面实现的”hello world”的功能。状态机Machine构建完成之后,需要调用initiate让它运行,并且进入初始状态Greeting。

Machine my_machine;
my_machine.initiate();

下面来实现稍微复杂的秒表功能,该秒表有两个按钮:开始/接收(Start/Stop) 和 重置(Reset), 对应有两个状态Stopped和Running。其状态图如下所示。

首先定义两个事件EvStartStop和EvReset,所有事件都要继承event

class EvStartStop : public boost::statechart::event< EvStartStop > {};
class EvReset : public boost::statechart::event< EvReset > {};

然后实现继承state_machine的秒表StopWatch状态机,其初始状态为Active。

class Active;
class StopWatch : public boost::statechart::state_machine< StopWatch, Active >
{
public:
    double ElapsedTime() const
    {
        return state_cast< const IElapsedTime & >().ElapsedTime();
    }
};

接着实现Active状态,m_dElapsedTime是记录当前秒表走的时长,simple_state接受四个参数,第一个参数当然就是Active本身,第二个参数因为Active是最外层的状态,所以要设置它所属的状态机为StopWatch,第三个参数则是设置Active的初始状态为Stopped。注意“typedef boost::statechart::transition< EvReset, Active > reactions; 的格式是固定的,表示如果收到EvReset事件,那么转移到Active状态。

/// simple_state 类模版接受四个参数:
/// 第二个参数 Active是最外层的状态,因此要被传递给所属的状态机
/// 第三个参数 指定内层的初始状态
/// 第四个参数 指定是否保存历史及保存何种历史
class Stopped;
class Active : public boost::statechart::simple_state< Active, StopWatch, Stopped >
{
public:
    typedef boost::statechart::transition< EvReset, Active > reactions;
    
    Active(): m_dElapsedTime(0.0) {}
    double ElapsedTime() const { return m_dElapsedTime; }
    double & ElapsedTime() { return m_dElapsedTime; }
    
private:
    double m_dElapsedTime;
};

定义IElapsedTime接口类,它由Running和Stopped两个状态来继承和实现

class IElapsedTime
{
public:
    virtual double ElapsedTime() const = 0;
};

实现Stopped状态,它指定Active为它的context, 这样它就会嵌套到Active中,这里实现的ElapsedTime函数,主要用于在Stopped状态下,StopWatch可以获取当前秒表的值。

class Stopped : public boost::statechart::simple_state< Stopped, Active >, public IElapsedTime
{
public:
    typedef boost::statechart::transition< EvStartStop, Running > reactions;
    virtual double ElapsedTime() const
    {
        return context< Active >().ElapsedTime();
    }
};

实现Running状态,同样的,它也指定Active为它的context, 这样它就会嵌套到Active中。注意Running状态下使用context<Active>则直接访问Running的直接外层状态Active.

class Running : public boost::statechart::simple_state< Running, Active >, public IElapsedTime
{
public:
    // 一个状态可以定义任意数量的反应,所以当反应多于一个时,要将它们放入一个mpl::list<>
    typedef boost::statechart::transition< EvStartStop, Stopped > reactions;
    
    Running(): m_StartTime(0) {}
    ~Running()
    {
        // context<>() 用于访问一个状态的直接或间接上下文
        // 这可以是直接或者间接外层状态或者状态机本身
        context< Active >().ElapsedTime() = ElapsedTime();
    }
 
    virtual double ElapsedTime() const
    {
        return context< Active >().ElapsedTime() +
        std::difftime( std::time( 0 ), m_StartTime );
    }
    
private:
    std::time_t m_StartTime;
};

完成秒表的所有实现之后,现在就可以编写测试代码来测试状态的转移情况。

StopWatch my_watch;
my_watch.initiate();
LOG(INFO) << "01 " << my_watch.ElapsedTime();
my_watch.process_event( EvStartStop() );
LOG(INFO) << "02 " << my_watch.ElapsedTime();
my_watch.process_event( EvStartStop() );
LOG(INFO) << "03 " << my_watch.ElapsedTime();
my_watch.process_event( EvStartStop() );
LOG(INFO) << "04 " << my_watch.ElapsedTime();
my_watch.process_event( EvReset() );
LOG(INFO) << "05 " << my_watch.ElapsedTime();

编译运行之后的打印信息如下,可以看出开始秒表的时长是0,发布EvStartStop事件之后,秒表的时长就不为0,当发布EvReset事件之后,秒表的时长再次变成0,说明重新进入了Active状态,m_dElapsedTime变量重置为0。

[void JTestBoost::TestStopwatch():302] 01 0
[void JTestBoost::TestStopwatch():304] 02 1.58486e+09
[void JTestBoost::TestStopwatch():306] 03 1.58486e+09
[void JTestBoost::TestStopwatch():308] 04 3.16971e+09
[void JTestBoost::TestStopwatch():310] 05 0

因为一个状态的context必须是一个完整的类型(即不可以是前向声明),所以状态机必须是由外而内进行定义,比如,上面秒表的总是从状态机(StopWatch)开始,接下来是外层的状态(Active), 最后才是外层状态的直接内层状态(Running/Stopped)。

秒表的功能已经介绍完成了,如果掌握了,就可以编写由几个状态的简单应用。对于稍多的状态,就需要“数码相机”登场了。一个状态可以由同一个事件触发的多个反应。这个就需要定制化反应。

假设一个数码相机由以下两个控制键,快门键和配置键。快门键分为快按和半按,对应事件为EvShutterHalf, EvShutterFull 和 EvShutterReleased;配置键对应事件为EvConfig。状态机图如下所示:

首先定义基本的事件,EvShutterHalf/EvShutterFull/EvShutterRelease/EvConfig/EvInFocus

class EvShutterHalf : public boost::statechart::event< EvShutterHalf > {};
class EvShutterFull : public boost::statechart::event< EvShutterFull > {};
class EvShutterRelease : public boost::statechart::event< EvShutterRelease > {};
class EvConfig : public boost::statechart::event< EvConfig > {};
class EvInFocus : public boost::statechart::event< EvInFocus > {};

实现数码相机的状态机Camera, 其初始状态为NotShooting

class NotShooting;
class Camera : public boost::statechart::state_machine< Camera, NotShooting >
{
public:
    bool IsMemoryAvailable() const { return true; }
    bool IsBatteryLow() const { return false; }
};

然后实现NotShooting状态,其初始内层状态为Idle,  注意我们这里使用了定制化反应custom_reaction,  这里只需指定事件,而实际的反应在react成员函数中实现。

class Idle;
class Shooting;
class NotShooting : public boost::statechart::simple_state<NotShooting, Camera, Idle >
{
public:
    typedef boost::statechart::custom_reaction< EvShutterHalf > reactions;
    boost::statechart::result react( const EvShutterHalf & )
    {
        LOG(INFO) << "cur NotShooting, receive EvShutterHalf";
        return transit< Shooting >();
    }
};

实现Idle状态,其外层状态为NotShooting

class Configuring;
class Idle : public boost::statechart::simple_state< Idle, NotShooting >
{
public:
    typedef boost::statechart::custom_reaction< EvConfig > reactions;
    boost::statechart::result react( const EvConfig & )
    {
        LOG(INFO) << "cur Idle, receive EvConfig";
        return transit< Configuring >();
    }
};

实现Configuring状态,其外层状态为NotShooting。

class Configuring : public boost::statechart::simple_state< Configuring, NotShooting >
{
public:
    typedef boost::statechart::custom_reaction< EvConfig > reactions;

    boost::statechart::result react( const EvConfig & )
    {
        LOG(INFO) << "cur Configuring, receive EvConfig";
        return transit< Idle >();
    }
};

实现Shooting状态,所属状态机Camera,  Shooting的初始状态为Focusing。

class Focusing;
class Shooting : public boost::statechart::simple_state< Shooting, Camera, Focusing >
{
public:
    typedef boost::statechart::custom_reaction< EvShutterRelease > reactions;
    
    boost::statechart::result react( const EvShutterRelease & )
    {
        LOG(INFO) << "cur Shooting, receive EvShutterRelease";
        return transit< Idle >();
    }
};

实现Focusing状态,其外层状态为Shooting。

class Focused;
class Focusing : public boost::statechart::simple_state< Focusing, Shooting >
{
public:
    typedef boost::mpl::list<
    boost::statechart::custom_reaction< EvInFocus >,
    boost::statechart::deferral< EvShutterFull >
    > reactions;
    boost::statechart::result react( const EvInFocus & )
    {
        LOG(INFO) << "cur Focusing, receive EvInFocus event";
        return transit< Focused >();
    }
};

实现Focused状态,其外层状态为Shooting。

class Storing;
class Focused : public boost::statechart::simple_state< Focused, Shooting >
{
public:
    typedef boost::mpl::list<
    boost::statechart::custom_reaction< EvShutterFull >> reactions;
    boost::statechart::result react( const EvShutterFull & )
    {
        if ( context< Camera >().IsMemoryAvailable() )
        {
            LOG(INFO) << "cur Focused, receive EvShutterFull event";
            return transit< Storing >();
        }
        else
        {
            std::cout << "Cache memory full. Please wait...\n";
            // 表示该事件可以被抛弃,因此,事件分派算法将停止查找反应,并且状态保存为Focused
            return discard_event();
        }
    }
};

实现Storing状态,其外层状态为Shooting。

class Storing : public boost::statechart::simple_state< Storing, Shooting >
{
public:
    Storing(){LOG(INFO) << "cur Storing";}
    ~Storing(){}
};

完成数码相机的所有实现之后,现在就可以开始进行验证效果。

Camera camera;
camera.initiate();

camera.process_event( EvShutterHalf() );
camera.process_event( EvShutterRelease() );
camera.process_event( EvConfig() );
camera.process_event( EvConfig() );

camera.process_event( EvShutterHalf() );
camera.process_event( EvInFocus() );

运行输出的信息如下,发布不同的事件,就会执行不同的反应,并且切换到其他状态。

[boost::statechart::result NotShooting::react(const EvShutterHalf &):344] cur NotShooting, receive EvShutterHalf
[boost::statechart::result Shooting::react(const EvShutterRelease &):385] cur Shooting, receive EvShutterRelease
[boost::statechart::result Idle::react(const EvConfig &):357] cur Idle, receive EvConfig
[boost::statechart::result Configuring::react(const EvConfig &):369] cur Configuring, receive EvConfig
[boost::statechart::result NotShooting::react(const EvShutterHalf &):344] cur NotShooting, receive EvShutterHalf
[boost::statechart::result Focusing::react(const EvInFocus &):401] cur Focusing, receive EvInFocus event

五、总结

至此,已经将基于迁移表实现的状态机,状态机设计模式以及boost statechart中的秒表和数码相机的功能介绍完毕。

迁移表实现的状态机关键点就是状态迁移关系正确存入映射表中,状态机设计模式则关键在于context上下文,其状态会持有该context对象指针,而boost statechart的状态机的实现,关键是要画出正确的状态图,然后依据状态图来定义事件、实现状态机、再实现外层状态,接着再实现内层状态。

这里介绍的状态机相关知识,只能说算是一个入门知识总结,更深入的议题还需要不断学习和实践来加深理解,比如异步状态机、历史、异常处理等。

六、参考资料

  1. boost startchart指南
  2. boost startchart原理
  3. <<敏捷软件开发 : 原则模式与实践>>

学会了这么神奇的模版模式,让你C++模版编程之路事半功倍

最近由于开发工作的需要,项目引入了boost::statechart的状态机,它大量了引用了CRTP,  它的全称是Curiously Recurring Template Pattern,奇异递归模版模式,C++模版编程中很常用的一种用法。那么它神奇的地方到底在哪里呢,接下来就一一来揭开它神秘的面纱。

一、奇异递归模版模式的简介

奇异递归模版模式的基本思想要点是:派生类要作为基类的模版参数。它是C++模版编程中常用的手法。理解它之后,学习模版编程过程中也会事半功倍,而不会觉得云里雾里的。

二、奇异递归模版模式的基本格式

奇异递归模版模式的基本格式如下:JCrtpDerived继承JCrtpBase,并且JCrtpDerived作为基类JCrtpBase的模版参数。通过这样的方式,基类就可以使用子类的方法。并且不需要使用到虚函数,一定程度上减少程序的开销。

template <typename T>
class JCrtpBase
{
public:
};

class JCrtpDerived : public JCrtpBase<JCrtpDerived>
{
public:

};

三、奇异递归模版模式的入门

从上面的给出的奇异递归模版模式的基本格式中可以看出,子类是作为基类的模版参数,但是如果传递给基类的模版参数不是基类的子类,那就会造成混乱错误。如下图所示,JCrtpDerived2子类继承了基类JCrtpBase,但是传递给基类的模版参数不是JCrtpDerived2。

template <typename T>
class JCrtpBase
{
public:
    void Do()
    {
        T* derived = static_cast<T *>(this);
    }

};

class JCrtpDerived1 : public JCrtpBase<JCrtpDerived1>
{
public:

};

class JCrtpDerived2 : public JCrtpBase<JCrtpDerived1>
{
public:

};

那么如何解决上面的问题呢,可以将基类的默认构造函数设置为私有,并且模版参数T设置为基类的友元。通过这样的方式,基类的构造函数只能由模版参数T调用。当创建JCrtpDerived2子类对象的时候,会调用基类的构造函数,而这时候发现JCrtpDerived2不是基类的友元,那么就无法调用基类构造函数而出错。

template <typename T>
class JCrtpBase
{
public:
    void Do()
    {
        T* derived = static_cast<T *>(this);
    }

private:
   JCrtpBase();
   friend T;
};

调用运行JCrtpDerived2,就会出现错误

JCrtpDerived1 crtp_derived1;
crtp_derived1.Do();

JCrtpDerived2 crtp_derived2;
crtp_derived2.Do();

四、奇异递归模版模式的应用场景

1、静态多态

奇异递归模版模式可以实现静态多态的效果,顾名思义,就是有多态的特性,但是不需要使用虚函数,是编译的时候确定,因此,能够减少运行时的开销。接下来就来看看两个示例。

基类JCrtpBase实现函数Do,该函数内部对象通过static_cast转换为模版参数对象,模版参数对象再调用对应的实现函数,而模版参数对象由子类来实现。

template <typename T>
class JCrtpBase
{
public:
    void Do()
    {
        T* derived = static_cast<T *>(this);
        derived->DoSomething();
    }

private:
    JCrtpBase(){}
    friend T;
};

class JCrtpDerived1 : public JCrtpBase<JCrtpDerived1>
{
public:
    void DoSomething()
    {
        LOG(INFO) << "I'am is JCrtpDerived1";
    }

};

class JCrtpDerived2: public JCrtpBase<JCrtpDerived2>
{
public:
    void DoSomething()
    {
        LOG(INFO) << "I'am is JCrtpDerived2";
    }
};

调用运行的效果如下所示,从中可以看出,对象调用基类的函数,而基类函数实际上又去调用子类的函数DoSomething。基于这样的思想,我们可以将通用的逻辑放在基类Do中实现,而不同的放到对应的子类函数DoSomething实现。

/// 调用   
JCrtpDerived1 crtp_derived1;
crtp_derived1.Do();

JCrtpDerived2 crtp_derived2;
crtp_derived2.Do();

/// 运行信息
[void JCrtpDerived1::DoSomething():33] I'am is JCrtpDerived1
[void JCrtpDerived2::DoSomething():43] I'am is JCrtpDerived2

这样需要注意的一点是,如果子类再被其他子类继承,那么其他子类就不能按照上面的方式实现。具体可以看下示例:JCrtpSub子类再继承JCrtpDerived1。

class JCrtpSub: public JCrtpDerived1
{
public:
    void DoSomething()
    {
        LOG(INFO) << "I'am is JCrtpSub";
    }
};

调用运行的效果如下所示,JCrtpSub调用基类的函数Do,但是运行没有调用到JCrtpSub类自身的函数DoSomething。

/// 调用
JCrtpDerived1 crtp_derived1;
crtp_derived1.Do();

JCrtpDerived2 crtp_derived2;
crtp_derived2.Do();

JCrtpSub ctrp_sub;
ctrp_sub.Do();

/// 运行信息
[void JCrtpDerived1::DoSomething():33] I'am is JCrtpDerived1
[void JCrtpDerived2::DoSomething():43] I'am is JCrtpDerived2
[void JCrtpDerived1::DoSomething():33] I'am is JCrtpDerived1

上面的例子是子类调用基类函数,由基类再转换调用子类函数,效果类似于策略模式。下面将要说明的例子,更像多态特性,但是不需要虚函数。基类和子类都实现相同的函数DoSomething

template <typename T>
class JCrtpBase
{
public:
    void DoSomething()
    {
        static_cast<T *>(this)->DoSomething();
    }

private:
    JCrtpBase(){}
    friend T;
};

class JCrtpDerived1 : public JCrtpBase<JCrtpDerived1>
{
public:
    void DoSomething()
    {
        LOG(INFO) << "I'am is JCrtpDerived1";
    }

};

class JCrtpDerived2: public JCrtpBase<JCrtpDerived2>
{
public:
    void DoSomething()
    {
        LOG(INFO) << "I'am is JCrtpDerived2";
    }
};

然后实现模版方法,该方法入参为基类JCrtpBase的引用,内部调用基类函数DoSomething。

template<typename T>
void DoAction(JCrtpBase<T> &ctrpbase)
{
    ctrpbase.DoSomething();
}

调用运行效果如下,向模版方法传递不同的子类,调用对应子类的函数。

/// 调用
JCrtpDerived1 crtp_derived1;
JCrtpDerived2 crtp_derived2;
DoAction(crtp_derived1);
DoAction(crtp_derived2);

// 打印信息
[void JCrtpDerived1::DoSomething():38] I'am is JCrtpDerived1
[void JCrtpDerived2::DoSomething():48] I'am is JCrtpDerived2

2、boost::statechart状态机

Boost.Statechart大量使用了CRTP,   派生类必须作为第一个参数传递给所有基类模版,Boost.Statechart状态机后续考虑作为一个专题来研究讨论。

struct Greeting : sc::simple_state< Greeting, Machine >

3、std::enable_shared_from_this特性

C++的特性enable_shared_from_this通常是用于在当你要返回一个被shared_ptr管理的对象。JObj继承enable_shared_from_this,并且JObj作为参数模版传递给enable_shared_from_this,这里就运用到了CRTP。

class JObj : public std::enable_shared_from_this<JObj>
{
public:
    std::shared_ptr<JObj> GetObj() {
        return shared_from_this();
    }
};

正确的调用方式,JObj是被shared_ptr管理,因此,如果要获取对象,那么JObj需要继承enable_shared_from_this。

std::shared_ptr<JObj> share_obj1 = std::make_shared<JObj>();
// JObj对象被shared_ptr管理,因此,如果要获取对象,那么JObj需要继承enable_shared_from_this
std::shared_ptr<JObj> share_obj2 = share_obj1->GetObj();

五、总结

到这里,奇异递归模版模式已经基本讲解完成,我们首先介绍了它的基本格式,使用注意要点,然后重点讲解了它的应用场景,包括静态多态、boost::statechart状态机、std::enable_shared_from_this特性。理解了奇异递归模版模式,不但有利于模版编程的学习,而且对于以后应用的开发也是有好处的。

设计模式详解十一:Flyweight模式

flyweight意思是轻量级,该模式主要为了解决有些场景会产生许多对象,并且其中的许多对象都是相同的。flyweight提供创建对象接口,并且内部保存对象池,当请求的对象没有在对象池中,那么重新创建对象放入对象池,如果请求的对象存在对象池中,那么直接从对象池取出对象返回给请求者,从而避免对象的频繁创建,造成系统开销的问题。

一、设计模式的结构图

Flyweight模式,FlyweightFactory根据key值提供获取Flyweight对象的接口,

二、程序代码实现

1、基类Flyweight的定义和实现

/// 类的定义
class Flyweight
{
public:
    virtual ~Flyweight();
    virtual void Operation(const std::string& strParam);
    std::string GetState();
    
protected:
    Flyweight(std::string strState);
private:
    std::string m_strState;
};

/// 类的实现
Flyweight::Flyweight(std::string strState)
{
    this->m_strState = strState;
}

Flyweight::~Flyweight()
{
}

void Flyweight::Operation(const std::string& strParam)
{
}
string Flyweight::GetState()
{
    return this->m_strState;
}

2、子类SubFlyweight的定义和实现

/// 类的定义
class SubFlyweight:public Flyweight
{
public:
    SubFlyweight(std::string strState);
    ~SubFlyweight();
    
    void Operation(const std::string& strParam);

protected:
private:
};


/// 类的实现
SubFlyweight::SubFlyweight(std::string strState):Flyweight(strState)
{
    DEBUG_LOG("SubFlyweight Build..... ", strState);
}

SubFlyweight::~SubFlyweight()
{
}

void SubFlyweight::Operation(const string& strParam)
{

}

3、FlyweightFactory工厂类的定义和实现

/// 类的定义
class FlyweightFactory
{
public:
    FlyweightFactory();
    ~FlyweightFactory();
    Flyweight* GetFlyweight(const std::string& key);
    
protected:
private:
    vector<Flyweight*> m_fly;
};


/// 类的实现
FlyweightFactory::FlyweightFactory()
{
}

FlyweightFactory::~FlyweightFactory()
{
}

Flyweight* FlyweightFactory::GetFlyweight(const std::string& key)
{
    vector<Flyweight*>::iterator it = m_fly.begin();
    for (; it != m_fly.end();it++)
    {
        if ((*it)->GetState() == key)
        {
            cout<<"already created by users...."<<endl;
            return *it;
        }
    }
    Flyweight* fw = new SubFlyweight(key);
    m_fly.push_back(fw);
    return fw;
}

三、测试验证结果

1、主程序输入如下所示的测试代码

FlyweightFactory* fc = new FlyweightFactory();
Flyweight* fw1 = fc->GetFlyweight("li");
Flyweight* fw2 = fc->GetFlyweight("zheng!");
Flyweight* fw3 = fc->GetFlyweight("li");

2、运行结果如下所示,第三次请求对象,因为已经存在在对象池中,所以直接从对象池中获取对象并返回

[2019-09-14 19:54:45.160  SubFlyweight:31]  SubFlyweight Build.....  = li
[2019-09-14 19:54:45.160  SubFlyweight:31]  SubFlyweight Build.....  = zheng!
already created by users....

四、总结

FlyweightFactory类采用vector存储对象,当然需要根据具体场景来选择合适的容器,比如,map、hash表等。需要注意的是FlyweightFactory查找的key值是存放在Flyweight对象中的成员变量。

设计模式详解十:外观模式

外观模式,即Facade模式,封装了内部细节,对外提供统一接口。比如做一件事的时候,需要经过几个步骤,但是使用者并不关心内部细节,因此,这时候就可以采用外观模式,将几个步骤封装起来,然后提供一个统一的接口。

一、设计模式的结构图

外观模式对外提供统一接口OperationWrapper, 内部封装两个子系统的接口。

二、程序代码实现

1、定义和实现子系统类1

/// 类的定义
class Subsystem1
{
public:
    Subsystem1();
    ~Subsystem1();
    void Operation();
    
protected:
private:
};


/// 类的实现
Subsystem1::Subsystem1()
{
}

Subsystem1::~Subsystem1()
{
}

void Subsystem1::Operation()
{
    DEBUG_LOG("", "Subsystem1 operation...");
}

2、定义和实现子系统类2

/// 类的定义
class Subsystem2
{
public:
    Subsystem2();
    ~Subsystem2();
    void Operation();

protected:
private:
};


/// 类的实现
Subsystem2::Subsystem2()
{
}

Subsystem2::~Subsystem2()
{
}
void Subsystem2::Operation()
{
    DEBUG_LOG("", "Subsystem2 operation...");
}

3、定义和实现外观模式

/// 类的定义
class Facade
{
public: Facade();
    ~Facade();
    void OperationWrapper();

protected:
private:
    Subsystem1* m_subs1;
    Subsystem2* m_subs2;
};


/// 类的实现
Facade::Facade()
{
    this->m_subs1 = new Subsystem1();
    this->m_subs2 = new Subsystem2();
}

Facade::~Facade()
{
    delete m_subs1;
    delete m_subs2;
}

void Facade::OperationWrapper()
{
    this->m_subs1->Operation();
    this->m_subs2->Operation();
}

从外观模式类看,它负责控制两个子系统类的创建和销毁,对外提供统一接口,内部实现调用两个子系统的函数。

三、测试验证结果

1、主程序输入如下所示的测试代码

Facade* facade = new Facade();
facade->OperationWrapper();

2、运行结果如下所示

[2019-09-08 16:16:18.993  Operation:21]   = Subsystem1 operation...
[2019-09-08 16:16:18.993  Operation:34]   = Subsystem2 operation...

四、总结

外观模式在实际中应用比较广泛,它通常采用单例模式实现,比如采用外观模式封装各个对象的创建过程。

设计模式详解九:迭代器模式

迭代器模式, 即iterator模式,封装集合对象,提供访问集合的接口。

一、设计模式的结构图

迭代器模式的处理逻辑是:iterator迭代器内部保存持有集合对象,用于访问集合,而集合提供创建返回迭代器的函数接口。

二、程序代码实现

1、 定义实现迭代器的基类

#ifndef Object
typedef int Object;
#endif


/// 类定义
class Iterator
{
public:
    virtual ~Iterator();
    virtual void First() = 0;
    virtual void Next() = 0;
    virtual bool IsDone() = 0;
    virtual Object CurrentItem() = 0;
    
protected:
    Iterator();
private:
};


/// 类实现

Iterator::Iterator()
{
    
}

Iterator::~Iterator()
{
    
}

2、 定义实现迭代器的子类

/// 类的定义
class Aggregate;
class ConcreteIterator : public Iterator
{
public:
    ConcreteIterator();
    ConcreteIterator(Aggregate *ag, int idx = 0);
    ~ConcreteIterator();
    void First();
    void Next();
    bool IsDone();
    Object CurrentItem();

protected:
private:
    Aggregate* m_ag;
    int m_idx;
};

/// 类的实现
ConcreteIterator::ConcreteIterator()
{
    
}

ConcreteIterator::ConcreteIterator(Aggregate *ag, int idx)
{
    this->m_ag = ag;
    this->m_idx = idx;
}

ConcreteIterator::~ConcreteIterator()
{
}

Object ConcreteIterator::CurrentItem()
{
    return m_ag->GetItem(m_idx);
}

void ConcreteIterator::First()
{
    m_idx = 0;
}

void ConcreteIterator::Next()
{
    if (m_idx < m_ag->GetSize())
    {
        m_idx++;
    }
}

bool ConcreteIterator::IsDone()
{
    return (m_idx == m_ag->GetSize());
}

3、定义实现集合的基类

#ifndef Object
typedef int Object;
#endif

/// 类的定义
class Iterator;
class Aggregate
{
public:
    virtual ~Aggregate();
    virtual Iterator* CreateIterator() = 0;
    virtual Object GetItem(int idx) = 0;
    virtual int GetSize() = 0;
    
protected:
    Aggregate();
private:
};


/// 类的实现
Aggregate::Aggregate()
{
}

Aggregate::~Aggregate()
{
}

4、定义实现集合的子类

/// 类的定义
class ConcreteAggregate:public Aggregate
{
public:
    enum {SIZE = 3};
    ConcreteAggregate();
    ~ConcreteAggregate();
    Iterator* CreateIterator();
    Object GetItem(int idx);
    int GetSize();
    
protected: private:
    Object m_objs[SIZE];
};


/// 类的实现
ConcreteAggregate::ConcreteAggregate()
{
    for (int i = 0; i < SIZE; i++)
        m_objs[i] = i;
}

ConcreteAggregate::~ConcreteAggregate()
{
}

Iterator* ConcreteAggregate::CreateIterator()
{
    return new ConcreteIterator(this);
}

Object ConcreteAggregate::GetItem(int idx)
{
    if (idx < this->GetSize())
    {
        return m_objs[idx];
    }
    else
    {
        return -1;
    }
}

int ConcreteAggregate::GetSize()
{
    return SIZE;
}

 

三、测试验证结果

1、主程序输入如下所示的测试代码

Aggregate* ag = new ConcreteAggregate();
Iterator* it = new ConcreteIterator(ag);
for (; !(it->IsDone()) ; it->Next())
{
    std::cout<< "item = "<< it->CurrentItem()<<std::endl;
}

2、运行结果如下所示,迭代器访问集合内的所有数据,并输出显示

item = 0
item = 1
item = 2

四、总结

迭代器的实现过程中,为了避免集合过多的暴露公有接口,可以将迭代器声明为集合的友元,来访问集合的私有数据。另外,从上面的代码实现中,可以看出集合提供创建迭代器的接口,而迭代器持有集合对象,内部包装集合的访问接口。

设计模式详解八:Starategy模式

strategy策略模式就是实现和抽象解耦,逻辑处理放到Context中,并在逻辑处理中调用抽象接口,而组合对象实现具体的内容。

一、设计模式的结构图

策略模式context实现主要逻辑,具体实现放到strategy类,strategy是抽象基类,子类继承该基类,实现具体的功能,context与strategy是一对多的组合关系,这样实现的好处就是context可以支持多个strategy类型对象,strategy类型也可以复用到其他不同的逻辑算法。

二、程序代码实现

1、定义实现抽象基类Strategy

class Strategy
{
public:
    Strategy();
    virtual ~Strategy();
    virtual void AlgrithmInterface() = 0;

protected:
private:
};

Strategy::Strategy()
{
}

Strategy::~Strategy()
{
    DEBUG_LOG("", "~Strategy...");
}

void Strategy::AlgrithmInterface()
{
}

2、定义实现基类Strategy的子类ConcreteStrategyA

class ConcreteStrategyA:public Strategy
{
public:
    ConcreteStrategyA();
    virtual ~ConcreteStrategyA();
    void AlgrithmInterface();

protected:
private:
};

ConcreteStrategyA::ConcreteStrategyA()
{
}

ConcreteStrategyA::~ConcreteStrategyA()
{
    DEBUG_LOG("", "~ConcreteStrategyA...");
}

void ConcreteStrategyA::AlgrithmInterface()
{
    DEBUG_LOG("", "ConcreteStrategyA::AlgrithmInterface...");
}

3、定义实现基类Strategy的子类ConcreteStrategyB

class ConcreteStrategyB:public Strategy {
public:
    ConcreteStrategyB();
    virtual ~ConcreteStrategyB();
    void AlgrithmInterface();
    
protected:
private:
};

ConcreteStrategyB::ConcreteStrategyB()
{
}

ConcreteStrategyB::~ConcreteStrategyB()
{
    DEBUG_LOG("", "~ConcreteStrategyB...");
}

void ConcreteStrategyB::AlgrithmInterface()
{
    DEBUG_LOG("", "ConcreteStrategyB::AlgrithmInterface...");
}

4、定义实现Context类

class Context
{
public:
    Context(Strategy* stg);
    ~Context();
    void DoAction();
protected:
private:
    Strategy* m_stg;
};

Context::Context(Strategy* stg)
{
    m_stg = stg;
}

Context::~Context()
{
    if (!m_stg)
        delete m_stg;
}
void Context::DoAction()
{
    m_stg->AlgrithmInterface();
}

三、测试验证结果

1、主程序输入如下所示的测试代码

Strategy* p_strategy;
p_strategy = new ConcreteStrategyA();
Context* p_context = new Context(p_strategy);
p_context->DoAction();
if (NULL != p_context)
{
    delete p_context;
    p_context = NULL;
}

2、运行结果如下图所示,Context对象内部最终调用ConcreteStrategyA对象的算法功能。

[2019-06-30 14:30:24.680  AlgrithmInterface:35]   = ConcreteStrategyA::AlgrithmInterface...

四、总结

策略模式,不变的逻辑算法放到Context对象中实现,而动态变化的算法实现则由Strategy子类来实现,这里有一点需要注意的是Context析构的时候,也会析构Strateby类型的对象,所以,外部定义的Strateby对象不需要再次进行delete。

设计模式详解七:Adapter模式

Adapter模式就是当已经设计好的接口与第三方库的接口不一致的时候,可以起到兼容的作用。Adapter模式一般分为:类模式(继承方式)和对象模式(组合方式)。

一、设计模式的结构图

1、类模式是采用继承的方式来复用Adaptee的接口

2、对象模式是采用组合的方式来调用Adaptee接口

二、程序代码实现

1、定义和实现用户的接口Target

//定义用户接口
class Target
{
public:
    Target();
    virtual ~Target();
    virtual void Request();
    
protected:
private:
};

//实现用户接口
Target::Target()
{
}

Target::~Target()
{
}

void Target::Request()
{
    DEBUG_LOG("", "Target::Request...");
}

2、 定义和实现第三方库接口Adaptee

//第三方
class Adaptee
{
public:
    Adaptee();
    ~Adaptee();
    void SpecificRequest();

protected:
private:
};


Adaptee::Adaptee()
{
}

Adaptee::~Adaptee()
{
}

void Adaptee::SpecificRequest()
{
    DEBUG_LOG("", "Adaptee::SpecificRequest...");
}

3、定义和实现类模式的适配器

//适配器1
class AdapterOne:public Target,private Adaptee
{
public:
    AdapterOne();
    ~AdapterOne();
    void Request();
    
protected:
private:
};


AdapterOne::AdapterOne()
{
}

AdapterOne::~AdapterOne()
{
}

void AdapterOne::Request()
{
    this->SpecificRequest();
}

4、定义和实现对象模式的适配器

//适配器2
class AdapterTwo:public Target
{
public:
    AdapterTwo(Adaptee* ade);
    ~AdapterTwo();
    void Request();

protected:
private:
    Adaptee* _ade;
};


AdapterTwo::AdapterTwo(Adaptee* ade)
{
    this->_ade = ade;
}

AdapterTwo::~AdapterTwo()
{
}

void AdapterTwo::Request()
{
    _ade->SpecificRequest();
}

三、测试验证结果

1、主函数输入如下所示的测试代码段,用来测试对象模式的适配器模式

Adaptee* ade = new Adaptee;
Target* adt = new AdapterTwo(ade);
adt->Request();

2、运行结果如下图所示,调用Request之后,内部实际上调用的是第三方库接口

[2019-06-23 13:51:20.713  SpecificRequest:36]   = Adaptee::SpecificRequest...

四、总结

适配器顾名思义就是一个转接口,将目标对象和第三方连接起来,Target与Adapter之间存在多态的特性,而Adapter内部调用Adaptee完成实际的功能。

设计模式详解六:装饰者模式

Decorator模式,即装饰者模式是通过组合的方式给类添加新的职责。通过组合的好处是不会增加类的继承深度。

一、设计模式的结构图

结构图中,SubComponent和Decorator都继承自同一个基类Component, SubComponet继承自Component基类,这个体现多态的特性,大家应该都明白。而Decorator也继承自Component基类是为什么呢?我的理解是,Decorator是为component添加新的职责,它应该是属于Component,后续才能同SubComponent一样的方式进行使用。Decorator作为SubDecorator的基类,是为了能够提供更多的修饰,比如后续有新的修饰SubDecorator2,那么原来的实现的接口是不需要修改的。

二、程序代码实现

1、定义和实现Component基类

class Component
{
public:
    virtual ~Component();
    virtual void Operation();
protected:
    Component();
private:
};


Component::Component()
{
}

Component::~Component()
{
}

void Component::Operation()
{
}

2、定义和实现SubComponent

class SubComponent:public Component
{
public:
    SubComponent();
    ~SubComponent();
    void Operation();

protected:
private:
};

SubComponent::SubComponent() {}

SubComponent::~SubComponent() {}

void SubComponent::Operation()
{
    DEBUG_LOG("", "SubComponent operation..." );
}

3、定义和实现Decorator类

class Decorator:public Component
{
public:
    Decorator(Component* com);
    virtual ~Decorator();
    void Operation();
    
protected:
    Component* m_com;
private:
};


Decorator::Decorator(Component* com)
{
    this->m_com = com;
}

Decorator::~Decorator()
{
    delete m_com;
}

void Decorator::Operation()  {}

4、定义和实现SubDecorator

class SubDecorator:public Decorator
{
public:
    SubDecorator(Component* com);
    ~SubDecorator();
    void Operation();
    void AddedBehavior();
    
protected:
private:
};


SubDecorator::SubDecorator(Component* com):Decorator(com)  {}

SubDecorator::~SubDecorator()  {}

void SubDecorator::AddedBehavior()
{
    DEBUG_LOG("", "SubDecorator::AddedBehacior...." );
}

void SubDecorator::Operation()
{
    m_com->Operation();
    this->AddedBehavior();
}

三、测试验证结果

程序输入如下所示的代码段,然后运行调试查看结果:

Component* com = new SubComponent();
Decorator* dec = new SubDecorator(com);
dec->Operation();
//com在dec在析构函数中释放
delete dec;

运行结果如下所示,SubDecorator内部调用SubComponent后,并且为SubComponent添加新的行为。

[2019-06-02 16:20:02.135  Operation:29]   = SubComponent operation...
[2019-06-02 16:20:02.135  AddedBehavior:53]   = SubDecorator::AddedBehacior....

四、总结

Decorator模式提供了一种添加职责的方式,采用组合的方式,因此,当需要添加操作的时候,可以考虑Decorator模式来解决。

备注:Decorator与Component之间是整体与部分的关系,一个Decorator可以对应多个Component对象。

设计模式详解五:Factory模式


创建Factory模式(工厂模式),主要有两个目的,(1)如果一个基类有很多的子类,那么可以通过Factory模式统一创建子类对象, 这样可以达到方便维护,提高扩展性的目的; (2) 将具体化类的工作延迟到子类中,举个例子:父类A会调用到类B,进行逻辑的判断处理,但是类B是一个抽象类,类A不知道类B的具体类是什么,当创建类A的子类C就可以知道类B的子类D,这样就可以达到将不变的逻辑放在父类处理,而将具体的实现放到子类中。

一、设计模式的结构图

1、Factory类提供创建类Product的接口CreateProduct,这是工厂模式的第一个作用:负责维护众多类的创建。

2、Factory类是抽象类,Product也是抽象类,可以在抽象类中做公共的处理,而具体的实现工作延迟到子类,这就是工厂模式的第二个作用。

二、程序代码实现

首先定义和实现生产基类和子类

1、定义生产基类和它的子类

class Product;

//生产基类
class Product
{
public:
    virtual ~Product() = 0;
    
protected:
    Product();
    
private:
};

//生产子类
class SubProduct:public Product
{
public:
    ~SubProduct();
    SubProduct();
    
protected:
private:
};

2、实现生产基类和它的子类

Product::Product()
{
}

Product::~Product()
{
}

SubProduct::SubProduct()
{
    DEBUG_LOG("", "SubProduct...");
}

SubProduct::~SubProduct()
{
}

 

下面是定义和实现工厂的基类和它的子类

1、定义工厂的基类和它的子类

//工厂基类
class Factory
{
public:
    virtual ~Factory() = 0;
    virtual Product* CreateProduct() = 0;
    
protected:
    Factory();
    
private:
};

//工厂子类
class SubFactory:public Factory
{
public:
    ~SubFactory();
    SubFactory();
    
    Product* CreateProduct();
protected:
    
private:
};

2、实现工厂的基类和它的子类

Factory::Factory()
{
}

Factory::~Factory()
{
}

SubFactory::SubFactory()
{
    DEBUG_LOG("", "SubFactory...");
}

SubFactory::~SubFactory()
{
}

Product* SubFactory::CreateProduct()
{
    return new SubProduct();
}

三、测试验证结果

程序输入如下所示的代码段,然后运行调试查看结果:

Factory *fac = new SubFactory();
Product *product = fac->CreateProduct();

delete fac;
delete product;

运行结果如下所示,构造具体的工厂对象,然后工厂对象创建具体的生产对象。

[2019-05-25 14:03:49.814  SubFactory:45]   = SubFactory...
[2019-05-25 14:03:49.814  SubProduct:27]   = SubProduct...

四、总结

工厂模式可以解决两个问题,第一个是负责对象的创建,第二个是将具体类的工作延迟到子类中。上面的示例中,通过SubFactory调用CreateProduct创建具体类SubProduct,如果这时候又要创建一个新的类SubProduct2,  那么有得在SubFactory中创建方法CreateProduct2来创建具体类SubProduct2,这样类SubFactory就封闭不了,那么解决的办法就是创建Factory的一个新的子类SubFactory2。另一个方式就是CreateProduct方法提供一个参数,然后通过参数来决定创建具体的Product。

设计模式详解四:Bridge模式

Bridge模式是将两个结合的对象进行解耦,以便达到一个对象的修改不会影响到另一个对象的目的。比如,不同操作系统和不同算法的两个对象,操作系统可以调用不同的算法,而不同的算法可以运行在不同的操作系统上。这里操作系统我们认为是抽象部分,算法是实现部分。

一、设计模式的结构图

Abstraction对应操作系统部分, Imp对应算法部分。Abstraction是操作系统的基类,SubAbstraction是该基类的子类,负责实现操作系统上的公共的部分。Imp是算法的基类,SubImpA和SubImpB是该基类的子类,负责实现算法功能。Abstraction和Imp之间是一对多的关系,一个操作系统可以支持多种算法。具体形象的结构图如下图:

二、程序代码实现

首先定义实现操作系统的部分。

1、定义操作系统的基类以及它的子类

//操作系统基类
class Abstraction
{
public:
    virtual ~Abstraction();
    virtual void Operation() = 0;

protected:
    Abstraction();
    private:
};

//具体操作系统的实现
class SubAbstraction:public Abstraction {
public:
    SubAbstraction(Imp* imp);
    ~SubAbstraction();
    void Operation();

protected:
    
private:
    Imp* m_imp;
};

2、实现操作系统的基类以及它的子类

Abstraction::Abstraction()
{
}
Abstraction::~Abstraction()
{
}


SubAbstraction::SubAbstraction(Imp* imp)
{
    m_imp = imp;
}

SubAbstraction::~SubAbstraction()
{
}

void SubAbstraction::Operation()
{
    m_imp->Operation();
}

然后定义实现算法的部分

1、定义算法的基类以及它的子类

//算法的基类
class Imp
{
public:
    virtual ~Imp();
    virtual void Operation() = 0;

protected:
    Imp();
private:
};

//具体算法的实现A
class SubImpA:public Imp
{
public:
    SubImpA(); ~SubImpA();
    virtual void Operation();
    
protected:
private:
};

//具体算法的实现B
class SubImpB:public Imp
{
public:
    SubImpB(); ~SubImpB();
    virtual void Operation();
    
protected:
private:
};

2、实现算法的基类以及它的子类

Imp::Imp()
{
}

Imp::~Imp()
{
}

void Imp::Operation()
{
    DEBUG_LOG("", "call Imp");
}


SubImpA::SubImpA()
{
}

SubImpA::~SubImpA()
{
}

void SubImpA::Operation()
{
    DEBUG_LOG("", "call SubImpA");
}


SubImpB::SubImpB()
{
}

SubImpB::~SubImpB()
{
}

void SubImpB::Operation()
{
    DEBUG_LOG("", "call SubImpB");
}

三、测试验证结果

主程序中输入如下的代码段,然后编译运行查看它的运行结果

Imp *imp = new SubImpA();
Abstraction *abs = new SubAbstraction(imp);
abs->Operation();

Imp *imp2 = new SubImpB();
Abstraction *abs2 = new SubAbstraction(imp2);
abs2->Operation();

运行结果如下所示,操作系统和算法的对象通过组合的方式结合,操作系统根据不同的算法对象调用不同的算法,而操作系统内部的处理逻辑是不需要改变的。

[2019-05-19 21:16:12.453  Operation:61]   = call SubImpA
[2019-05-19 21:16:12.453  Operation:73]   = call SubImpB

四、总结

Bridge模式的原理是采用组合的方式,将两个对象结合起来,两个对象各自实现内部的处理逻辑,但是对外接口保持不变。比如上面的操作系统和算法两个对象,操作系统结合C++的多态特性,调用算法的接口,而算法接口不变,根据不同的算法内部实现不同的算法处理逻辑。当增加新的操作系统的时候,算法不需要修改。当增加新的算法的时候,操作系统也不需要修改。真正实现两个对象解耦的目的。