libssh2を使用してSSHコマンドを実行するためのC++クラスを作成しています。
libssh2 SSHセッションのライフサイクルは、次の段階を経ます。
- 初期化(ローカルリソースを取得)
- ハンドシェイク/認証(リモートホストでSSHセッションを確立します)
- 切断(リモートホストでのSSHセッションを終了します)
- 無料(ローカルリソースを解放します。必要に応じて、手順3も実行します)。
ステップ1の前に、ステップ2でlibssh2に渡すソケットを開く必要があります。それ以降、libssh2はソケットへの参照を格納するため、ソケットを渡す必要はありません。手順4の後、ソケットを閉じることができます。
これをクラス(SSHSession
)で公開しています。セットアップ(ステップ1と2)をctorで実行し、ティアダウン(ステップ3と4)をdtorで実行したいと思います(ステップ2は時間がかかるため、これはセッションのプールを開いたままにして、コマンドを実行するために再利用できるようにします)。
私の最初の試みはすべてのコードをに集中させSSHSession
、そのctorはすぐに混乱し、「このステップが失敗した場合は、すでに行われたことを確認して元に戻す必要があります」ルーチンでした。dtorはそれほど複雑ではありませんでしたが、それでも「忙しすぎる」と感じました。
次に、作業をいくつかのクラスに分割し、取得/リリースの各ステップにRAIIを実装しました。
- 手順1と4。
- ステップ2および3。
SessionConnection
手順2と3を実装するクラスを作成し、SessionHandle
手順1と4を実装するタイプのメンバーを作成しました。また、データメンバーとしてソケットがあり、ctor/dtorへの1次依存関係が作成されましたSessionHandle
。メンバーの前にソケットを破棄することはできませんでした。
デザインを検討しているときに、ステップ2と3を次のように配置できると思いました。
2.1。ハンドシェイク(リモートホストでSSHセッションを確立します)
2.2。認証
3.切断(リモートホストでのSSHセッションを終了します)
つまり、クラスをさらに単純化してSessionConnection
、ステップ2.1と3でRAIIを実行する別のクラスを実装し、最終的に次のようにすることができます。
- クラス
SSHSession
にはSessionConnection
データメンバーがあります。 - クラス
SessionConnection
はステップ2.2を実装します。 SessionConnection
ソケットデータメンバーがあります。SessionConnection
手順1と4を実装するSessionHandle
データメンバーがあります。これは、ソケットの前に破棄する必要があります。SessionConnection
RemoteSessionHandle
ステップ2.1および3を実装するデータメンバーがあります。これは、データメンバーの後に作成し、SessionHandle
データメンバーの前に破棄する必要があります。
これにより、RAIIのおかげで、私のctors/dtorsが大幅に簡素化されます。そして、私は概念的に健全だと思います。一方では、SSHセッションが通過する状態としてこれらを見ることができ、他方では、管理しているリソース(ローカル、リモート)としても見ることができます。
しかし、私は現在、厳密な建設/破壊の順序をSessionConnection
持っており、それは改善であると信じていますが(そして、「これは悪である」と述べた私の研究では何も見つかりませんでしたが、「これは明確に文書化されるべきです」だけです)、私は興味があります他の意見では、私はこのデザインの可能な代替案についての情報/ポインターを喜んで受け入れます。
私がこれまで見てきたこと:
- ScopeGuard、ScopeExit。これも同様のメカニズムで、デザインの改善に使用できるものは見つかりませんでした。
- ステートマシン。それを使って設計を単純化する方法を見つけることができず、RAIIが行う1つの問題、つまり、何かが失敗した場合のクリーンアップを解決できませんでした。
御時間ありがとうございます。