使用OOP实施API约束

时间:2011-06-07 14:49:50

标签: oop api

我有一个编码器类。编码器可以处于三种状态:新创建,编码或完成。有三种名为startEncoding()appendFrame()finishEncoding()的方法,每种方法只能在编码器处于相关状态时才被调用(在开始编码之前插入帧没有意义或你完成后)。我想知道如何强制执行这些约束。

目前我在内部跟踪状态并在方法开头声明正确的状态:

void appendFrame() {
    assert(state == STATE_ENCODING);
    …
}

这是一个低成本,有效的解决方案。但它对我来说似乎不是最佳选择,因为生成的API容易被误用(比如在附加帧之前忘记启动编码器)并且断言有点愚蠢(额外的代码,如果不是其他任何东西)。

我想知道也许我可以将班级分成三个对应于三个州的小班级。然后API会很明显,因为每个类只包含支持的方法。但是这个解决方案显然感觉就像是出汗太多了,而且我不知道如何处理状态切换,比如从正在运行的编码器到完成的编码器。

你能想到另一种解决方案比在方法中手动检查状态更好吗?这种用例是否有模式?

4 个答案:

答案 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"