Давайте рассмотрим эту проблему в перспективе.
В теории программирования существуют концепции создания и уничтожения объекта и попытки и завершения процедуры. Оба в конечном итоге связаны с управлением ресурсами, но они сосредоточены на разных вещах.
- С объектами мы обращаемся к потенциально долгоживущему дереву объектов и переменных, на которые ссылается указатель объекта.
- С помощью процедур мы обращаемся к обычно временным объектам и переменным, существующим в области вызова процедуры (заметным исключением является main).
Теперь, в зависимости от процедуры, нам может понадобиться создать ряд ресурсов, которые, если процедура будет прервана фатальной ошибкой, должны откатить все порожденные ресурсы в обратном порядке. Довольно часто этого проще всего добиться, создавая объекты, предназначенные для управления соответствующим ресурсом. Ada.Finalization становится здесь весьма полезным.
Но это может быть излишним, и могут быть аргументы против этой техники в каждом конкретном случае. Итак, если мы заинтересованы в автономном управлении ресурсами для процедуры и использовании ключевого слова finally в стиле C++, рассмотрите следующее.
Я часто сначала был доволен обещанием удобства использования finally, но потом разочаровывался тем, насколько сложным может оказаться код после его использования. Вложение требуется для сложных операций отката, и во многих случаях процесс не является линейным, требуя логики, чтобы решить, как именно выполнить откат, в зависимости от того, как далеко мы продвинулись через инициализацию. Структура вложенных блоков заставляет вас использовать линейную логику. Таким образом, когда вам нужно что-то более сложное, вы серьезно рассматриваете возможность использования goto.
Я прошел через это достаточно раз, чтобы понять, что, как бы ни была аппетитна финализация в стиле ООП с тем, что я упомянул, настоящая попытка и, наконец, могут быть достигнуты в Аде без всех головных болей, как показано в следующем примере. Обратите внимание, что она далека от совершенства, но я думаю, что хорошая стратегия перевешивает плохую. Что мне особенно нравится в этой стратегии, так это то, насколько явным становится процесс финализации, все шаги помечены и проверены на соответствие системе типов Ады, хорошо организованы и просты в управлении. Метод try & finally в стиле C++ рассеивает такой код, а Finalize в Аде скрывает его и затрудняет управление высокоуровневой логикой аварийного переключения.
procedure Proc is
type Init_Stages_All is (Do_Null, Do_Place, Do_Pour, Do_Drink);
subtype Init_Stages is Init_Stages_All range Do_Place .. Do_Drink;
Init_Stage : Init_Stages_All := Do_Null;
procedure Initialize_Next is
begin
Init_Stage := Init_Stages_All'Succ(Init_Stage);
case Init_Stage is
when Do_Place => ...
when Do_Pour => ...
when Do_Drink => ...
when Do_Null => null;
end case;
end Initialize_Next;
procedure Finally is
begin
for Deinit_Stage in reverse Init_Stage .. Init_Stages'Last loop
case Deinit_Stage is
when Do_Place => ...
when Do_Pour => ...
when Do_Drink => ...
end case;
end loop;
end Finally;
begin
Initialize_Next; -- Do_Place
...
Initialize_Next; -- Do_Pour
...
Initialize_Next; -- Do_Drink
...
Finally;
exception
when E : others =>
...
Finally;
raise;
end Proc;
И наконец, эта стратегия также упрощает работу с исключениями финализации. Я бы предложил создать процедуру в Finally, которая вызывается при возникновении исключения, и рекурсивно вызывать finally, манипулируя Init_Stage, а также вставляя туда любую дополнительную логику отработки отказа.
person
NiGHTS
schedule
16.02.2021
Finalize
управляемого объекта больше похож на деструктор. он совсем не похож на блок finalize. - person Adrien Plisson   schedule 26.01.2011finally
, который не имеет ничего общего с вызовом методовfinalize
. - person Gabe   schedule 26.01.2011