Страницы

Поиск по вопросам

воскресенье, 15 марта 2020 г.

Транзакции в коде

#алгоритм #разработка_игр #исключения #best_practice


Есть примерно такой код:

ПриВыходеИзВарпРежима()
{
  ДобавитьКораблиВКосмос();
  УстановитьУНихДефолтныеКоординаты();
  ...
  ВключитьЩиты();
  ОбнаружитьПротивников();
}


В методе ВключитьЩиты произошла ошибка, а значит, я хочу всё откатить назад.
С базами данных всё легко, там есть транзакции. А как быть с кодом? 
Может придумали что-то? Чтобы не писать кучу обратных шагов.
    


Ответы

Ответ 1



Попробуйте написать ваш код в стиле «опасные изменения — безопасный коммит» + иммутабельность. // изменения локальные корабли' = создать корабли с дефолтными координатами и включённым щитом(); локальный космос' = космос.ДобавитьКораблиИВернутьНовыйКосмос(корабли'); ... локальные противники' = обнаружить противников в (космос'); // безопасный коммит космос = космос' противники = противники' Если какая-то часть из изменений вылетит — она затрагивает лишь локальные объекты, которые съест garbage collector или RAII. Если все объекты у вас иммутабельны, то новый локальный космос — не расходная штука: он делит большую часть своих объектов со старым космосом.

Ответ 2



Для решения такой задачи используется паттерн Memento (Хранитель). С его помощью Вы сохраняете состояние объекта перед началом операций. А в случае ошибки - записываете сохраненное состояние обратно. Осталось только отловить наличие ошибки. Беспроигрышный вариант - через возвращаемые значения. Еще можно воспользоваться исключениями. Но в данном случае они не очень к месту, так как это вовсе даже не исключительная ситуация, а вполне себе игровая логика. Они только добавят лишние тормоза. И размажут логику по коду не хуже, чем это делает goto.

Ответ 3



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

Ответ 4



Я видел такую методику. Перед выполнением критичного кода, делаем копию всех переменных/объектов. Потом выполняем код. Если произошла ошибка, то просто восстанавливаем все назад. В С++ это можно сделать удобно, если написать правильные конструкторы копирования. В чистом Си я видел другой прием. Хитрая череда goto. Где то так. ПриВыходеИзВарпРежима() { if (!ДобавитьКораблиВКосмос()) { goto space; } if (!УстановитьУНихДефолтныеКоординаты()) { goto coord; } ... if (!ВключитьЩиты()) { goto guard; } if (!ОбнаружитьПротивников()) {goto anemi;} return ok; anemi: ПровестиДиагностикуВторогоУровня(); guard: ПерекалиброватьЩиты(); coord: УбратьДефолтныеКоординаты(); space: УбратьКораблиСКосмоса(); return bad; } То есть, если происходит ошибка - переходим по goto на обработчики "откатки в к нормальному состоянию". Да, я вижу, что Вы не хотите писать "обратные шаги", но иногда без них просто невозможно.

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

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