我有一个编码器类。编码器可以处于三种状态:新创建,编码或完成。有三种名为startEncoding()
,appendFrame()
和finishEncoding()
的方法,每种方法只能在编码器处于相关状态时才被调用(在开始编码之前插入帧没有意义或你完成后)。我想知道如何强制执行这些约束。
目前我在内部跟踪状态并在方法开头声明正确的状态:
void appendFrame() {
assert(state == STATE_ENCODING);
…
}
这是一个低成本,有效的解决方案。但它对我来说似乎不是最佳选择,因为生成的API容易被误用(比如在附加帧之前忘记启动编码器)并且断言有点愚蠢(额外的代码,如果不是其他任何东西)。
我想知道也许我可以将班级分成三个对应于三个州的小班级。然后API会很明显,因为每个类只包含支持的方法。但是这个解决方案显然感觉就像是出汗太多了,而且我不知道如何处理状态切换,比如从正在运行的编码器到完成的编码器。
你能想到另一种解决方案比在方法中手动检查状态更好吗?这种用例是否有模式?
答案 0 :(得分:2)
另一种解决方案是在方法的内部或外部检查过渡。
你有一个状态机,有一组状态,还有一组转换。您可以允许呼叫者先验地询问是否允许特定转换。其中一个用途是在用户界面中启用控件。
无论如何,内部检查仍然很有用。例外可能比无效转换的断言更好。
如果您支持多个设备或算法,对于允许的转换具有相似但不同的规则,则此方法会很有用。
答案 1 :(得分:1)
您可以通过创建以前状态的依赖关系来强制执行状态转换。使实际工作的方法受到保护,并以一种强制调用它们的方式包装调用。例如:
class foo {
public:
static foo* start() { foo* f = new foo; f->doStart(); return f;}
static void doit(foo* f) { f->doDoit(); }
static void finish(foo* f) { f->doFinish(); delete f; }
protected:
void doStart() { std::cout << "doStart()\n"; }
void doDoit() { std::cout << "doDoit()\n"; }
void doFinish() { std::cout << "doFinish()\n"; }
};
int main()
{
foo* f = foo::start();
foo::doit(f);
foo::finish(f);
return 0;
}
在doit()
成功返回之前,无法调用方法start()
。在您的示例中,您没有指定在appendFrame()
之前是否必须至少调用一次finish()
,但如果是这种情况,您也可以在那里创建一个额外的依赖项。
答案 2 :(得分:0)
通常一个对象有一个“生命周期”,有三个主要方法(类别),一个用于构造函数或初始化对象,一个用于对象的主要操作,另一个用于析构函数或对象的最终化。 / p>
如果一个类/对象有更多的方法,通常适合我提到的3个类别中的一个。
如果你想设计一个O.O. A.P.I.,您可能想申请的一个限制因素是,您的A.P.I公开了这3个方法/方法类别。用户。
您在示例中使用状态或转换。我认为你不需要子类,因为它是一个非常简单的类。通常,当使用状态机类时,您可能需要一个 public 函数,该函数返回其对象的当前状态。
(C ++风格示例,可能会修改为你的程序。)
class MyStatusMachineClass {
protected:
int _CurrentStatus = 0;
public:
int currentStatus(); // <-- main operation category method
void startEncoding(); // <-- not a constructor, but works like one
void appendFrame(); // <-- main operation category method
void finishEncoding(); // <-- not a destructor, but works like one
} // end class
受状态约束的方法必须在执行其操作之前调用此函数。您可能还想添加在生成错误时要执行的操作。
特别是,如果A.P.I.用户想要调用startEncoding(),并且该对象不在它应该的状态。
有几种方法可以处理错误,例如异常,但是在API的情况下,我建议当发现错误时,程序不会中断,但内部字段变量会存储错误的代码,一个公共函数返回该值
(C ++风格示例,可能会修改为你的程序。)
class MyStatusMachineClass {
protected:
int _CurrentStatus = 0;
int _LastErrorCode = 0; // <-- "0" means "no error"
public:
int currentStatus(); // <-- main operation category method
int LastErrorCode();
void startEncoding(); // <-- not a constructor, but works like one
void appendFrame(); // <-- main operation category method
void finishEncoding(); // <-- not a destructor, but works like one
} // end class
void main()
{
MyStatusMachineClass* myStatusMachineObject = new MyStatusMachineClass();
myStatusMachineObject->appendFrame();
if (myStatusMachineObject->LastErrorCode()) {
cout << "Error: Cannot append frame in current status.";
}
delete myStatusMachineObject();
}
干杯。
答案 3 :(得分:0)
对我来说似乎是"State pattern"。