この問題を展望してみましょう。
プログラミング理論には、オブジェクトの作成と破棄、およびプロシージャの試行と最終の概念があります。どちらも最終的にはリソース管理を扱いますが、異なることに重点を置いています。
- オブジェクトを使用して、オブジェクトポインタによって参照されるオブジェクトと変数の潜在的に長寿命のツリーを中心にピボットします。
- プロシージャでは、プロシージャ呼び出しのスコープ内に存在する通常は一時的なオブジェクトと変数を軸にします(注目すべき例外はmainです)
ここで、手順によっては、一連のリソースを生成する必要がある場合があります。致命的なエラーによって手順が中断された場合は、生成されたすべてのリソースを逆の順序でロールバックする必要があります。多くの場合、これは、それぞれのリソースの管理専用のオブジェクトを作成することで最も簡単に実現できます。Ada.Finalizationはここで非常に便利になります。
しかし、これはやり過ぎである可能性があり、ケースバイケースでこの手法に反対する議論が行われる可能性があります。したがって、プロシージャに対する自己完結型のリソース管理に関心があり、C ++スタイルのfinallyキーワードを使用する場合は、次のことを考慮してください。
私は最初は便利さの約束に満足していましたが、最終的には、コードを使用した後のコードの複雑さにがっかりしました。複雑なロールバック操作にはネストが必要であり、多くの場合、プロセスは線形ではなく、初期化をどこまで行ったかに応じて、ロールバックの方法を正確に決定するロジックが必要です。ネストされたブロック構造は、線形論理を使用するためにあなたを追い詰めます。したがって、より複雑なものが必要な場合は、gotoの使用を真剣に検討しています。
次の例に示すように、OOPスタイルのファイナライズと同じくらい食欲をそそるのは、私が述べたとおりであるため、Adaですべての頭痛の種なしに、真の試みと最終を達成できることを理解するのに十分な時間を経験しました。注意してください、それは完璧にはほど遠いですが、私はこの戦略で良いものが悪いものを上回っていると思います。この戦略で私が特に気に入っているのは、ファイナライズプロセスがいかに明確になり、すべてのステップがAdaの型システムに対してラベル付けされてチェックされ、適切に編成され、管理しやすいことです。C ++スタイルのtry&finallyはこの種のコードを分散させ、Adaの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でプロシージャを作成し、Init_Stageを操作して、そこに追加のフェイルオーバーロジックを挿入することにより、Finallyを再帰的に呼び出すことをお勧めします。