Применение ограничений API с использованием ООП - PullRequest
1 голос
/ 07 июня 2011

У меня есть класс кодировщика. Кодировщик может находиться в трех состояниях: только что созданный, кодированный или готовый. Существует три метода с именами startEncoding(), appendFrame() и finishEncoding(), каждый из которых может быть вызван только тогда, когда кодировщик находится в соответствующем состоянии (нет смысла вставлять кадры до тех пор, пока вы не начнете кодировать или не закончите). Мне интересно, как эти ограничения могут быть реализованы.

В настоящее время я отслеживаю внутреннее состояние и утверждаю правильное состояние в начале методов:

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

Это простое, рабочее решение. Но он не кажется мне оптимальным, поскольку результирующий API легко использовать неправильно (например, забыть запустить кодировщик перед добавлением кадров), а утверждения немного глупы (дополнительный код, если не что-нибудь еще).

Мне было интересно, что, возможно, я смогу разделить класс на три меньших класса, соответствующих трем состояниям. Тогда API будет очевидным, поскольку каждый класс будет содержать только поддерживаемые методы. Но это решение, очевидно, похоже на то, что оно слишком потеет, и я не уверен, как справиться с переключением состояний, как переход от работающего кодера к окончательному.

Можете ли вы придумать другое решение, которое было бы лучше, чем проверка состояния вручную в методах? Есть ли шаблон для такого варианта использования?

Ответы [ 4 ]

2 голосов
/ 07 июня 2011

Другое решение заключается в проверке переходов , как внутренних, так и внешних к методам.

У вас есть конечный автомат с набором состояний и набором переходов между ними. Вы можете позволить вызывающей стороне априори спросить, разрешен ли конкретный переход. Одним из применений этого является включение элементов управления в пользовательском интерфейсе.

Несмотря на это, все равно полезно проверить внутренне. Исключения могут быть лучше, чем утверждения о недопустимых переходах.

Этот подход становится полезным, если вы поддерживаете несколько устройств или алгоритмов с похожими, но разными правилами для допустимых переходов.

1 голос
/ 07 июня 2011

Вы можете применять переходы между состояниями, создавая зависимости от предыдущих состояний.Сделайте методы, выполняющие реальную работу, защищенными, и оберните вызовы таким образом, чтобы обеспечить, как они могут быть вызваны.Например:

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(), но в этом случае вы могли бы также создать дополнительную зависимость.

0 голосов
/ 08 июня 2011

Похоже на "Государственный паттерн" для меня.

0 голосов
/ 07 июня 2011

Обычно объект имеет «жизненный цикл» с 3 основными методами (категориями), один для конструктора или инициализации объекта, один для основной операции объекта и один для деструктора или завершения объекта.

Если у класса / объекта есть больше методов, обычно это соответствие одной из трех упомянутых мной категорий.

Если вы хотите создать OOAPI, одно из ограничений, вы можете применитьявляется то, что эти 3 категории методов / методов являются общедоступными для ваших пользователей API.

В вашем примере вы используете состояния или переходы.Я не думаю, что вам нужно подкласс, потому что это очень простой класс.Обычно, когда используется класс конечного автомата, вам может потребоваться функция public , которая возвращает текущее состояние объекта.

(пример стиля C ++ может изменить ваш progr. lang.)

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

Ваши методы, которые ограничены состоянием, должны вызвать эту функцию перед выполнением своей операции.Вы также можете добавить, что делать в случае возникновения ошибки.

Особенно, если пользователь API хочет вызвать startEncoding (), а объект находится не в том состоянии, в котором он должен быть.

Существует несколько способов обработки ошибок, например, исключений, но в случае API я рекомендую, чтобы при обнаружении ошибки программа не прерывалась, но во внутренней переменной поля был сохранен код ошибки, открытыйфункция возвращает это значение

(пример стиля C ++, возможно, измените ваш прогр. lang.)

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...