среда, 28 марта 2012 г.

Идиомы. Нейтральный к исключениям код на C#.

Под нейтральным к исключениям кодом понимается такой код, в котором не предумотрена явная обработка возможных исключений (try/catch) и который при этом в состояние справиться с возникшими исключениями.

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

Реализация подобной схемы дает несколько преимуществ:
1) Улучшение читабельности кода за счет отсутствия конструкций try/catch/finnally.
2) Код гарантированно не генерирует исключения после изменения состояния обьектов, что вполне может произойти в блоках catch или finally.
3) Если возникает исключение до изменения состояния обьектов - все изменения откатываются и обьекты остаются в первоначальном состоянии.

Минусы подобной схемы:
1) Снижение эффективностикода во время выполнения за счет большего количества потребляемых системных ресурсов.

Расмотрим пример реализации подобного кода:



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


using System.Collections;

class Item { }   //класс, описывающий один товар

class ItemsDB   //база товаров
{
     private ArrayList   depotItems;      //список товаров на складе
     private ArrayList   martItems;        //список товаров в торговом зале

     //функция удаляет товар со склада
     private void DeleteItemFromDepot(int index)
     {
           //клонируем списки, чтобы в случае исключения восстановить их
           ArrayList  tempDepotItems = (ArrayList)depotItems.Clone();
           ArrayList  tempMartItems   =  (ArrayList)martItems.Clone();

           //выполняем действия над временными обьектами 
           object item = tempDepotItem[index];  //выбираем товар со склада
           tempDepotItem.RemoveAt(index);  //удаляем товар со склада
           tempMartItems.Add(item);  //добавляем товар в список товаров в торговом зале

           //фиксируем изменения в нашей базе товаров
           ArrayList tempSpace = null;    //временый список для обмена значениями списков товаров
           ListSwap(ref depotItems, ref tempDepotItems, ref tempSpace);
           ListSwap(ref martItems, ref tempMartItems, ref tempSpace);
     } 

     //функция осуществляет обмен значениями между двумя списками
     void ListSwap(ref  ArrayList firstList,  ref  ArrayList secondList,  ref  ArrayList tempList)
     {
             tempList   = firstList;
             firstList  = secondList;
             secondList = tempList;
             tempList   = null;
     }
}

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

Заметим, что функция ListSwap гарантированно не генерирует исключения, т.к. содержит только присваивание ссылок. Таким образом, если обмен значениями временных списков прошёл успешно, она всегда выполнит фиксацию произведенных изменений в первончальных списках. Данная функция не содержит также операций создания новых обьектов, чтобы исключить возможность переполнения стека - StackOverflowExeption. То етсь, функция фиксации измений написана так, что даже теоретически не может вызвать исключение.

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

Комментариев нет:

Отправить комментарий