5

ユーザーが文字列を入力し、ネットワーク経由で送信できるコマンドラインインターフェイスを備えたプログラムがあります。問題は、GUI の奥深くで生成されるイベントをネットワーク インターフェイスに接続する方法がわからないことです。たとえば、私の GUI クラス階層が次のようになっているとします。

GUI -> MainWindow -> CommandLineInterface -> EntryField

各 GUI オブジェクトは他のいくつかの GUI オブジェクトを保持し、すべてが非公開です。entryField オブジェクトは、メッセージが入力されたことを示すイベント/シグナルを生成します。現時点では、信号をクラス階層に渡しているため、CLI クラスは次のようになります。

public:
    sig::csignal<void, string> msgEntered;

そしてc'torで:

entryField.msgEntered.connect(sigc::mem_fun(this, &CLI::passUp));

passUp 関数は、メイン ループで最終的にこれを実行できるようになるまで、所有するクラス (MainWindow) に接続するためのシグナルを再度発行します。

gui.msgEntered.connect(sigc::mem_fun(networkInterface, &NetworkInterface::sendMSG));

今、これは本当に悪い解決策のようです。GUI に何かを追加するたびに、クラス階層全体に接続する必要があります。これを回避する方法はいくつかあります。すべてのオブジェクトをパブリックにすることで、メイン ループでこれを実行できます。

gui.mainWindow.cli.entryField.msgEntered.connect(sigc::mem_fun(networkInterface, &NetworkInterface::sendMSG));

しかし、それはカプセル化の考え方に反します。GUI 全体でネットワーク インターフェイスへの参照を渡すこともできますが、GUI コードはできるだけ分離したいと考えています。

ここで本質的な何かが欠けているように感じます。これを行うためのきれいな方法はありますか?

注:私は GTK+/gtkmm/LibSigC++ を使用していますが、Qt でほぼ同じ問題が発生したため、そのようにタグ付けしていません。それは本当に一般的な質問です。

4

6 に答える 6

9

根本的な問題は、GUI をモノリシック アプリケーションのように扱っていることです。GUI だけが、通常よりも大きなワイヤを介して残りのロジックに接続されています。

GUI がバックエンド サーバーと対話する方法を再考する必要があります。一般に、これは GUI がスタンドアロン アプリケーションになることを意味します。このアプリケーションはほとんど何もせず、GUI の内部 (信号やイベントなど) とサーバーの処理ロジックを直接結合することなくサーバーと通信します。つまり、ボタンをクリックしたときに何らかのアクションを実行したい場合があります。その場合、サーバーを呼び出す必要がありますが、他のほとんどすべてのイベントは、GUI 内の状態を変更するだけで、サーバーには何もしません。準備ができているか、ユーザーが何らかの応答を求めているか、またはバックグラウンドで呼び出しを行うのに十分なアイドル時間があります。

秘訣は、GUI とは完全に独立してサーバーのインターフェースを定義することです。サーバーをまったく変更せずに、後で GUI を変更できるはずです。

つまり、イベントを自動的に送信することはできず、手動で関連付ける必要があります。

于 2009-02-03T09:25:48.080 に答える
3

Observer設計パターンを試してください。リンクには現在のサンプルコードが含まれています。

あなたが見逃している本質的なことは、その参照がオブジェクトが実装するインターフェース(抽象クラ​​ス)としてキャストされている場合、カプセル化に違反することなく参照を渡すことができるということです。

于 2009-02-03T09:17:58.893 に答える
2

グローバルなパブ/サブハブがない限り、階層の上下に何かを渡すことから逃れることはできません。リスナーを汎用インターフェイスまたはコントローラーに抽象化する場合でも、何らかの方法でコントローラーをUIイベントにアタッチする必要があります。

pub / subハブを使用すると、別の間接層を追加できますが、それでも重複があります。entryFieldには「メッセージ準備完了イベントの公開」と表示され、リスナー/コントローラー/ネットワークインターフェイスには「メッセージ準備完了イベントのリッスン」と表示されるため、一般的なものがあります。双方が知る必要のあるイベントID。2か所でハードコーディングしない場合は、両方のファイルに渡す必要があります(ただし、グローバルとしては引数として渡されません。それ自体はそうではありません。 t大きな利点)。

私は4つのアプローチすべて(直接結合、コントローラー、リスナー、およびpub-sub)を使用しました。各後継では、結合を少し緩めますが、重複のIDだけであっても、重複から逃れることはできません。公開されたイベント。

それは本当に分散に帰着します。インターフェイスの別の実装に切り替える必要がある場合は、具体的なインターフェイスをコントローラーとして抽象化する価値があります。状態を監視する他のロジックが必要な場合は、それをオブザーバーに変更します。プロセス間で分離する必要がある場合、またはより一般的なアーキテクチャにプラグインする必要がある場合、pub / subは機能しますが、グローバル状態の形式を導入し、コンパイル時のチェックには適していません。

ただし、システムの各部分を個別に変更する必要がない場合は、心配する価値はありません。

于 2009-02-03T09:34:25.460 に答える
2

これは一般的な質問なので、私は「ただの」Java プログラマですが、答えようと思います。:)

私は、プログラムの両側でインターフェイス (抽象クラスまたは対応するメカニズムが C++ にあるもの) を使用することを好みます。一方には、ビジネス ロジックを含むプログラム コアがあります。たとえば、GUI クラスが受信できるイベントを生成できます。一方、コアは、「stringEntered」などのメソッドを含む「UI リスナー」インターフェースを実装します。

このようにして、UI はビジネス ロジックから完全に分離されます。適切なインターフェイスを実装することで、コアと UI の間にネットワーク層を導入することもできます。

[編集] 私のアプリケーションのスターター クラスには、ほとんどの場合、次のようなコードがあります。

Core core = new Core(); /* Core implements GUIListener */
GUI gui = new GUI(); /* GUI implements CoreListener */
core.addCoreListener(gui);
gui.addGUIListener(core);

[/編集]

于 2009-02-03T09:17:19.910 に答える
0

私の意見では、CLI は GUI から独立しているべきです。MVC アーキテクチャでは、モデルの役割を果たす必要があります。

EntryField と CLI の両方を管理するコントローラーを配置します。EntryField が変更されるたびに、CLI が呼び出され、これらすべてがコントローラーによって管理されます。

于 2009-02-03T09:18:19.360 に答える
0

任意の GUI を分離し、一時的な仮想パックを使用してメッセージと簡単に通信できます。このプロジェクトもチェックしてください。

于 2015-06-12T07:33:43.517 に答える