Как обрабатывать несколько действий и исключений - PullRequest
0 голосов
/ 20 марта 2011

Я хотел бы знать, есть ли исключительное решение для следующей проблемы:

Ниже у меня есть SomeClass, который имеет функцию-член, которая может выполнять несколько действий (команд).Отдельные действия выполняются другой функцией-членом, которая всегда оставляет объект в хорошем и предсказуемом состоянии.

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

Я вижу эти решения, но ни одно из них не кажется хорошим:

a) Передайте исключение вызывающей стороне ExecuteMultipleCommands.Это оставляет объект в хорошем, но непредсказуемом состоянии (не знаю, какие действия были выполнены).

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

c) При первом исключении попробуйте отменить уже выполненные действия, чтобы объект вернулся ксостояние перед вызовом «ExecuteMultipleCommands».Теперь во время «отката» может произойти другое исключение.

Приведенный ниже код не является реальным кодом, но должен показать мою проблему:

class SomeClass
{
  public:
  struct Command
  {
    /*...*/
  };

  void ExecuteOneCommand( const Command &oneCommand )
  {
      /* either completely executes a command or throws exception and leave object in unchanged state */
  }

  void ExecuteMultipleCommands( const vector< Command > &commands )
  {
      vector< Command >::const_iterator it = commands.begin();
      for ( ; it != commands.end(); ++it )
      {
          try
          {
              ExecuteOneCommand( *it );
          }
          catch( /* some exception type */ )
          {
              /* what to do ? */
          }
      }
  }
};

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

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

Также команды могут зависеть от текущего состояния объекта.Как вы должны сначала добавить пару ключ / значение на карту, прежде чем вы сможете изменить значение.Это не означает, что «команда изменения» всегда должна сочетаться с «командой добавления», поскольку ключ также может уже присутствовать.

Ответы [ 4 ]

1 голос
/ 20 марта 2011

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

Я бы работал с копиями ваших объектов.

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

Я бы такжеconceder инкапсулирует ваши команды: под этим я имею в виду список команд, которые должны выполняться вместе.

таким образом вы можете преобразовать свой код в нечто вроде

  class SomeClass
    {
      public:
      struct Command
      {
        /*...*/
      };

      void ExecuteOneCommand( const Command &oneCommand )
      {
          /* either completely executes a command or throws exception and leave object in unchanged state */
      }

      SomeClass ExecuteCommands( const vector< Command > &commands )
      {
      SomeObject save = getCopy();
      try
      {

         ExecuteMultipleCommands(commands);
      }catch( /* some exception type */ )
      {
              return save
      }
      return this;    
      }

 void ExecuteMultipleCommands( const vector< Command > &commands )
      {

          vector< Command >::const_iterator it = commands.begin();
          for ( ; it != commands.end(); ++it )
          {
                  ExecuteOneCommand( *it );
           }
      }
    };

ПравитьЯ просто понял, что это работает лучше, если вы извлекаете часть сохранения из класса:

SomeObject save = someObjectInstance.copy();
if(!someObjectInstance.executeCommands){
//free someObjectInstance
someObjectInstance = save;
}
0 голосов
/ 20 марта 2011

Как это обычно работает, работая с копиями. Например, LINQ to SQL в .NET будет работать с копиями в памяти, а затем обрабатывать все изменения сразу и откатывать все изменения при любом сбое.

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

0 голосов
/ 20 марта 2011

Я думаю, что только вызывающий ExecuteMultipleCommands может в достаточной степени обработать исключение. Чтобы упростить обработку исключений, вы можете изменить блок catch в ExecuteMultipleCommands и добавить дополнительную информацию к исключению:

void ExecuteMultipleCommands( const vector< Command > &commands )
{
    size_t command_index  = 0;
    try 
    {
       for ( command_index = 0; command_index < commands.size() ; command_index++)
       {
            ExecuteOneCommand(commands[command_index]);
       }
    }
    catch (OneCommandException &e)
    {
        MultipleCommandException new_exception = MultipleCommandException(e); 
       // assuming MultipleCommandException has a public constructor that accepts OneCommandException&
        new_exception.command_index = command_index;
        throw new_exception;
    }
}
0 голосов
/ 20 марта 2011

Я не знаю, есть ли лучший способ сделать это.Это зависит от того, что делают ваши команды.Если некоторые команды зависят от других, у вас должен быть список зависимостей в каждой команде и флаг в каждой команде, указывающий на завершение.После того, как команда вызвана, она должна просмотреть зависимости, чтобы убедиться, что все они завершены перед запуском.Если вам нужны команды для «отката», вы можете просто сделать копию команды для ее запуска и, если она выполняется без исключений, скопировать временную команду в исходную и установить для true значение true.Если вам необходимо выполнить откат в случае сбоя зависимого компонента, вам, возможно, потребуется сохранить копию оригинала, приложенную к завершенному оригиналу, скопировать ее обратно и пометить как не завершенную в случае сбоя зависимого элемента.Трудно понять ваши требования, не зная больше о вашем приложении.

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