Сведения о вопросе

HEIGTH

18:27, 17th August, 2020

Теги

exception    

Обработка исключений: контракт против исключительного подхода

Просмотров: 414   Ответов: 5

Я знаю два подхода к обработке исключений, давайте посмотрим на них.

  1. Контрактный подход.

    Когда метод не делает то, что он говорит, что он будет делать в заголовке метода, он будет вызывать исключение. Таким образом, метод "promises", что он будет выполнять операцию, и если он не работает по какой-то причине, он будет выдавать исключение.

  2. Исключительный подход.

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

Позволяет использовать оба подхода в разных случаях:

У нас есть класс Customer, который имеет метод под названием OrderProduct.

контрактный подход:

class Customer
{
     public void OrderProduct(Product product)
     {
           if((m_credit - product.Price) < 0)
                  throw new NoCreditException("Not enough credit!");
           // do stuff 
     }
}

исключительный подход:

class Customer
{
     public bool OrderProduct(Product product)
     {
          if((m_credit - product.Price) < 0)
                   return false;
          // do stuff
          return true;
     }
}

if !(customer.OrderProduct(product))
            Console.WriteLine("Not enough credit!");
else
   // go on with your life

Здесь я предпочитаю исключительный подход, так как это не является действительно исключительным, что у клиента нет денег, если он не выиграл в лотерею.

Но вот ситуация, в которой я ошибаюсь в стиле контракта.

Исключительный:

class CarController
{
     // returns null if car creation failed.
     public Car CreateCar(string model)
     {
         // something went wrong, wrong model
         return null;
     }
 }

Когда я вызываю метод с именем CreateCar, я чертовски хорошо ожидаю экземпляр Car вместо какого-то паршивого указателя null, который может разрушить мой рабочий код через дюжину строк. Поэтому я предпочитаю контракт этому:

class CarController
{

     public Car CreateCar(string model)
     {
         // something went wrong, wrong model
         throw new CarModelNotKnownException("Model unkown");

         return new Car();
     }
 }

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



  Сведения об ответе

DINO

18:30, 25th August, 2020

Я предпочитаю то, что вы называете подходом "contract". В языке, поддерживающем исключения, нет необходимости возвращать значения null или другие специальные значения, указывающие на ошибки. Я считаю, что гораздо легче понять код, когда он не имеет кучу предложений "if (result == NULL)" или "if (result == -1)", смешанных с тем, что может быть очень простой, простой логикой.


  Сведения об ответе

FAriza

03:55, 16th August, 2020

Мой обычный подход заключается в использовании контракта для обработки любого вида ошибки из-за вызова "client", то есть из-за внешней ошибки (т. е. ArgumentNullException).

Каждая ошибка в аргументах не обрабатывается. Возникает исключение, и "client" отвечает за его обработку. С другой стороны, для внутренних ошибок Всегда старайтесь их исправить (как будто вы не можете получить соединение с базой данных по какой-то причине) и только если вы не можете справиться с этим, повторите исключение.

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


  Сведения об ответе

repe

00:37, 5th August, 2020

Я считаю, что если вы создаете класс, который будет использоваться внешней программой (или будет повторно использоваться другими программами), то вы должны использовать контрактный подход. Хорошим примером этого является API любого рода.


  Сведения об ответе

PIRLO

07:06, 28th August, 2020

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


  Сведения об ответе

ASER

13:27, 15th August, 2020

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

Обратите внимание, что некоторые ситуации могут быть или не быть исключительными в зависимости от того, что ожидает вызывающий код. Если вызывающий ожидает, что словарь будет содержать определенный элемент, и отсутствие этого элемента будет указывать на серьезную проблему, то неспособность найти элемент является исключительным условием и должна вызвать исключение. Если, однако, вызывающий объект на самом деле не знает, существует ли элемент, и одинаково готов обрабатывать его присутствие или его отсутствие, то отсутствие элемента будет ожидаемым условием и не должно вызывать исключение. Лучший способ справиться с такими изменениями в ожиданиях вызывающего абонента - это указать в контракте два метода: метод DoSomething и метод TryDoSomething, например

TValue GetValue(TKey Key);
bool TryGetValue(TKey Key, ref TValue value);

Обратите внимание, что, хотя стандартный шаблон 'try', как показано выше, некоторые альтернативы также могут быть полезны, если вы разрабатываете интерфейс, который производит элементы:

 // In case of failure, set ok false and return default<TValue>.
TValue TryGetResult(ref bool ok, TParam param);
// In case of failure, indicate particular problem in GetKeyErrorInfo
// and return default<TValue>.
TValue TryGetResult(ref GetKeyErrorInfo errorInfo, ref TParam param);

Обратите внимание, что использование чего-то вроде обычного шаблона TryGetResult в интерфейсе сделает интерфейс инвариантным по отношению к типу результата; использование одного из шаблонов выше позволит интерфейсу быть ковариантным по отношению к типу результата. Кроме того, это позволит использовать результат в объявлении 'var':

  var myThingResult = myThing.TryGetSomeValue(ref ok, whatever);
  if (ok) { do_whatever }

Не совсем стандартный подход, но в некоторых случаях преимущества могут оправдать его.


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться