简体   繁体   English

使用 OOP 强制执行 API 约束

[英]Enforcing API constraints using OOP

I have an encoder class.我有一个编码器 class。 The encoder can be in three states: freshly created, encoding or finished.编码器可以处于三种状态:新创建、编码或完成。 There are three methods named startEncoding() , appendFrame() and finishEncoding() that can each only be called when the encoder is in the relevant state (it does not make sense to insert frames until you start encoding or after you finish).有三个方法分别命名为startEncoding()appendFrame()finishEncoding() ,每个方法只能在编码器位于相关 state 中时调用(在开始编码或完成之后插入帧是没有意义的)。 I am wondering about how these constraints can be enforced.我想知道如何执行这些约束。

Currently I track the state internally and assert the right state at the beginning of the methods:目前我在内部跟踪 state 并在方法开始时断言正确的 state:

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

It's a low-hassle, working solution.这是一个省心、有效的解决方案。 But it does not seem like an optimal one to me, since the resulting API is easy to misuse (like forgetting to start the encoder before appending frames) and the asserts are a bit dumb (extra code, if not anything else).但这对我来说似乎不是一个最佳选择,因为生成的 API 很容易被滥用(比如在附加帧之前忘记启动编码器)并且断言有点愚蠢(额外代码,如果不是其他的话)。

I was wondering that maybe I could split the class into three smaller classes corresponding to the three states.我想知道也许我可以将 class 分成三个较小的类,对应于三个状态。 Then the API would be obvious, since each class would only contain the supported methods.那么 API 将是显而易见的,因为每个 class 将只包含支持的方法。 But this solution obviously feels like sweating it too much, and I'm not sure how to handle state switching like going from a running encoder to a finished one.但是这个解决方案显然让人觉得汗流浃背,而且我不确定如何处理 state 切换,就像从正在运行的编码器切换到完成的编码器一样。

Can you think of another solution that would be better than checking the state manually in the methods?您能想出比在方法中手动检查 state 更好的解决方案吗? Is there a pattern for such use case?这种用例有模式吗?

Another solution is to check transitions , either internally or externally to methods.另一种解决方案是检查方法内部或外部的转换

You have a state machine, with a set of states, and a set of transitions between.您有一台 state 机器,具有一组状态和一组转换。 You could allow a caller to ask a priori whether a particular transition is allowed.您可以允许调用者先验地询问是否允许特定转换。 One use of this is to enable controls in a user interface.它的一种用途是在用户界面中启用控件。

Regardless, it's still useful to check internally.无论如何,内部检查仍然有用。 Exceptions might be better than assertions for invalid transitions.异常可能比无效转换的断言更好。

This approach becomes helpful if you support multiple devices or algorithms, with similar but different rules for allowable transitions.如果您支持多种设备或算法,这种方法会很有帮助,允许的转换具有相似但不同的规则。

You could enforce state transitions by creating dependencies on previous states.您可以通过创建对先前状态的依赖关系来强制执行 state 转换。 Make the methods that do the actual work protected and wrap the calls in a way that will enforce how they may be invoked.使执行实际工作的方法受到保护,并以强制执行调用方式的方式包装调用。 For example:例如:

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;
}

The method doit() cant be invoked until start() returns successfully.start()返回成功之前,方法doit()不能被调用。 In your example you don't specify if appendFrame() must be called at least once before finish() but if that is the case you could create an additional dependency there as well.在您的示例中,您没有指定appendFrame()是否必须在finish()之前至少调用一次,但如果是这种情况,您也可以在那里创建一个额外的依赖项。

Usually an object has a "life cycle" with 3 main methods (categories), one for the constructor or initialization of the object, one for main operation of the object, and one for the destructor or finalization of the object. Usually an object has a "life cycle" with 3 main methods (categories), one for the constructor or initialization of the object, one for main operation of the object, and one for the destructor or finalization of the object.

If a class / object has more methods, usually the fit in one of the 3 categories I mention.如果 class / object 有更多方法,通常适合我提到的 3 个类别之一。

If you want to design an OOAPI, one of the constraints, you may want to apply, is that there are those 3 methods / methods categories public to your API users.如果您想设计一个 OOAPI,您可能想要应用的约束之一是您的 API 用户可以公开这 3 个方法/方法类别。

You are using states or transistions in you example.您在示例中使用状态或转换。 I don't think you need to subclass, because its a very simple class.我认为您不需要子类化,因为它是一个非常简单的 class。 Usually, when a state machine class is used, you may require a public function that returns which its the current status of the object.通常,当使用 state 机器 class 时,您可能需要一个公共function 来返回其当前状态 ZA2669CFDE63131BDC459。

(C++ style example, may have modify to your progr. lang.) (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

Your methods that are constrained by the status, must call this function before perform its operation.您的受状态约束的方法,必须在执行其操作之前调用此 function。 You may also want to add what to do in case a error is generated.您可能还想添加在生成错误时要执行的操作。

Specially, if the API user wants to call startEncoding(), and the object is not in the status it should be.特别是,如果 API 用户想要调用 startEncoding(),而 object 不在应有的状态。

There are several ways to handle errors, like exceptions, but in case of API, I recommend that when an error is found, the program is not interrupted, but an internal field variable stores a code for the error, an a public function returns that value有几种处理错误的方法,比如异常,但在 API 的情况下,我建议当发现错误时,程序不会中断,而是内部字段变量存储错误代码,a public function 返回价值

(C++ style example, may have modify to your progr. lang.) (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();
}

Cheers.干杯。

Seems like "State pattern" to me.对我来说似乎是“状态模式”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM