将Java状态机示例转换为C++,陷入最后一道障碍



背景

在中断了一段时间之后,我又回到了C++领域。我在C或C++方面从来都不是很出色,但我认为自己有一点工作知识。

我以一个简单的目标出发。将Java状态机示例转换为C++我认为问题是在传递new State<CeilingFanPullChain>时,我已经向从模板接口继承的类添加了空的存根构造函数(用于解决可能应该通过更改设计来解决的依赖性问题)。

作为我自己的要求,我不想使用预处理器魔法,这似乎是许多在线示例所使用的。我也不想使用switch语句。我想让它尽可能地面向对象。

#include <iostream>
#include <string>
// The CeilingFanPullChain class is now a wrapper
// that delegates to its m_current_state reference.
// Each clause from the "before" case statement is
// now captured in a State derived class.
// For this simple domain, the State pattern is
// probably over-kill.

template <class T>
class State {
public:
virtual void pull( T &wrapper ) = 0;
};
class CeilingFanPullChain {
private:
State<CeilingFanPullChain>* m_current_state;
public:
CeilingFanPullChain() {
m_current_state = new Off();
}
void set_state( State<CeilingFanPullChain>* s ) {
m_current_state = s;
}
void pull() {
m_current_state->pull( *this );
}
};
class Off: public State<CeilingFanPullChain> {
public:
Off() {}
void pull( CeilingFanPullChain &wrapper ) {
wrapper.set_state( new Low() );
std::cout << "   low speedn";
}
};
class Low: State<CeilingFanPullChain> {
public:
Low() {}
void pull( CeilingFanPullChain &wrapper ) {
wrapper.set_state( new Medium() );
std::cout << "   medium speedn";
}
};
class Medium: State<CeilingFanPullChain> {
public:
Medium() {}
void pull( CeilingFanPullChain &wrapper ) {
wrapper.set_state( new High() );
std::cout << "   high speedn";
}
};
class High: State<CeilingFanPullChain> {
public:
High() {}
void pull( CeilingFanPullChain &wrapper ) {
wrapper.set_state( new Off() );
std::cout << "   turning offn";
}
};
std::string get_line() {
std::string line_tmp;
std::getline(std::cin, line_tmp);
return line_tmp;
}
int main() {
CeilingFanPullChain* chain = new CeilingFanPullChain();
while (true) {
std::cout << "Press n";
get_line();
chain->pull();
}
return 0;
}

错误输出

ceiling-fan.cpp: In constructor ‘CeilingFanPullChain::CeilingFanPullChain()’:
ceiling-fan.cpp:26:31: error: expected type-specifier before ‘Off’
m_current_state = new Off();
^
ceiling-fan.cpp: In member function ‘virtual void Off::pull(CeilingFanPullChain&)’:
ceiling-fan.cpp:40:32: error: expected type-specifier before ‘Low’
wrapper.set_state( new Low() );
^
ceiling-fan.cpp: In member function ‘virtual void Low::pull(CeilingFanPullChain&)’:
ceiling-fan.cpp:49:32: error: expected type-specifier before ‘Medium’
wrapper.set_state( new Medium() );
^
ceiling-fan.cpp: In member function ‘virtual void Medium::pull(CeilingFanPullChain&)’:
ceiling-fan.cpp:58:32: error: expected type-specifier before ‘High’
wrapper.set_state( new High() );

我已经解决了自己的问题。

#include <iostream>
#include <memory>
#include <string>
#include <map>
#include <boost/algorithm/string.hpp>
// The CeilingFanPullChain class is now a wrapper
// that delegates to its m_current_state reference.
// Each clause from the "before" case statement is
// now captured in a State derived class.
// For this simple domain, the State pattern is
// probably over-kill.

template <class T>
class State {
public:
virtual void pull( T &wrapper ) = 0;
};
class CeilingFanPullChain {
private:
std::shared_ptr<State<CeilingFanPullChain>> m_current_state;
std::map <std::string, std::shared_ptr<State<CeilingFanPullChain> > >& m_states;
public:
CeilingFanPullChain(std::map <std::string, std::shared_ptr<State<CeilingFanPullChain> > >& states) : m_states(states) {
m_current_state.reset();
m_current_state = m_states["Off"];
}
void set_state( const std::string new_state) {
if(m_states.find(new_state) != m_states.end()) {
m_current_state.reset();
m_current_state = m_states[new_state];
}
}
void pull() {
m_current_state->pull( *this );
}
~CeilingFanPullChain() {
}
};
class Off: public State<CeilingFanPullChain> {
public:
Off() {}
void pull( CeilingFanPullChain &wrapper ) {
wrapper.set_state( "Low" );
std::cout << "low speed" << std::endl;
}
};
class Low: public State<CeilingFanPullChain> {
public:
Low() {}
void pull( CeilingFanPullChain &wrapper ) {
wrapper.set_state( "Medium" );
std::cout << "medium speed" << std::endl;
}
};
class Medium: public State<CeilingFanPullChain> {
public:
Medium() {}
void pull( CeilingFanPullChain &wrapper ) {
wrapper.set_state( "High" );
std::cout << "high speed" << std::endl;
}
};
class High: public State<CeilingFanPullChain> {
public:
High() {}
void pull( CeilingFanPullChain &wrapper ) {
wrapper.set_state( "Off" );
std::cout << "turning off" << std::endl;
}
};
class Program {
private:
std::map <std::string, std::shared_ptr<State<CeilingFanPullChain> > > m_state_list;
CeilingFanPullChain* m_chain = 0;
public:
Program() {
m_state_list["Off"] = std::make_shared<Off>();
m_state_list["Low"] = std::make_shared<Low>();
m_state_list["Medium"] = std::make_shared<Medium>();
m_state_list["High"] = std::make_shared<High>();
m_chain = new CeilingFanPullChain(m_state_list);
}
void run() {
int i = 0;
show_help();
while (true) {
std::string Input = get_line();
std::transform(Input.begin(), Input.end(), Input.begin(), tolower);
if( Input.compare(quit) == 0) {
std::cout << "Exiting" << std::endl;
break;
}
if( Input.compare(pull) == 0 ) {
m_chain->pull();
i++; 
} else if( Input.compare(help) == 0) {
show_help();
} else {
std::cout << "Sorry, I didn't get that, please make a choice from the options shown" << std::endl;
}
}
if( i > 3 ) { 
std::cout << "Wow... you changed the fan " << i << " times! Are you okay?" << std::endl;
}
std::cout << "Bye..." << std::endl;
}
~Program() {
m_state_list.clear();
delete m_chain;
}
void show_help() {
std::cout << "Enter a command, and then press <Enter>" << std::endl;
std::cout << pull << " : pull-chain (next setting)" << std::endl;
std::cout << quit << " : exit" << std::endl;
std::cout << help << " : show help" << std::endl;
}
std::string get_line() {
std::string line_tmp;
std::getline(std::cin, line_tmp);
return line_tmp;
}
protected:
std::string const quit = "q";
std::string const pull = "p";
std::string const help = "h";
};
int main() {
auto p = std::make_unique<Program>();
p->run();
return 0;
}

我使用了std::map,这样我就不会创建一个知道它所有状态(或创建它们)的CeilingFanPullChain;而是依赖于与我的模板类类型兼容的状态。

我还把主程序制作成了自己的课程;这样我就可以浏览、发现并清除内存泄漏(我相信其中有一些,我很乐意指出)。

我只是对自己有点失望,因为现在它已经编译好了,我可能根本不会使用它。首先,状态机是无限期运行的。无限while循环应该运行,直到设置了某个预先确定的条件。

要在linux机器上编译,我使用

g++ -std=gnu++14 -Wall -pedantic ceiling-fan.cpp -o ceiling-fan

我喜欢这个解决方案的地方

  • 它编译(总是很好)
  • 从上往下读很容易
  • 它不直接使用预处理器或内联类定义

可以改进什么

  • 垃圾收集
  • 示例代码的有用性
  • 代码的分离
  • 对此开放接受更多建议

最新更新