- - PR -
イベントの伝播
投稿者 | 投稿内容 | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2008-05-16 15:15
うーむー。
私が短く書くとやっぱり伝わらないですねぇ。意味不明です。 わかりづらくてすみません>読んでる人全員。 私の思考過程をここに書くのはかなり大変ですが、 なるべく簡単に、書いてみます。 #私のやり方ですから、いいかどうかは知りません。 まず…。 データの包含関係とUIの包含関係が一致している範囲ではその階層にあわせて コンポーネントを作っていきます。 これは問題ないですね。 件の仕様のように、データの包含関係とUIの包含関係が一致していない場合は、 複数のコンポーネントが密接に関わることになります。 避けたいところですが、仕様上しようがないのであれば、 それはその密接に関わったコンポーネント全体でひとつのコンポーネントとして、 密結合なコンポーネントを作成します。 今回はUserControlA、UserControl1、UserControl2、UserControl3が 密接に関わっていますから、 それらを包含するコンテナ(今回はForm)を密結合なコンポーネントとします。 密結合で、必ず「全体として一つ」として使いますから、 互いに参照を持ち合っても、なんら不具合はありません。 Formのコンストラクタもしくはイニシャライザで 内部を初期化する際に、互いの参照を持ち合います。 (子は親の参照のみを持つのが普通ですが。) 内部の通信は、その参照を使います。 イベントでもできますが、 パフォーマンス上もコーディングの単純さからも、 無駄ですので私は使いません。 イベントはコンポーネントが外部と通信するためにのみ使います。 ですので、 「Private Event」は私は滅多に使いませんし、 「Protected Event」もほぼ使いません。 また、コンポーネントを作るときに将来のことを考えて いくつかのイベントは外部に公開しておきます。 UpdatedとかXXXStateChangedとか。 コンポーネントを作る際のベースのコンテナですが、 Formというのはコンテナであると同時に、 独立したトップレベルウィンドウになりうる特殊なコントロールですので、 ちょっと特殊な位置づけです。 そのときの状況に応じて、 ・Formの上にそのまま作る ・UserControlやPanelの継承などで、独自定義のコントロールとして作る の2者から選びます。 (後者は詳細には何通りかあるのはご存知の通り) 再利用する際に前者ならFormまるごと、 後者ならFormに載せるコントロールとして再利用することになります。 後者で作って、Formにそのコントロールしか載せない、というもよくやります。 そんな感じで作ります。 DOS時代とかWin16時代とか、遥か昔の癖も引きずってますので、 ベストではないと思いますが、指針程度にはなるかと。 以下、 indigo-xさんの書き込み (2008-05-16 12:52) と otfさんの書き込み (2008-05-16 13:40) への返信がぐちゃぐちゃになりますが。
上で書いたように、イベントではなく参照を保持して、 その参照からInternal(friend)な関数・メソッドを呼びます。
そうです。
上で書いたように、避けたいことなどありません。 互いに依存することはごく普通にあります。 互いに依存しないように作れるのなら、 それははじめから互いに依存しない独立したコンポーネントにできるということを意味します。 それができないので問題になってるわけですから、 あきらめて互いに密に依存したコンポーネントにします。 ただ、依存はなるべくツリー型にしておきます。 というか、自然とそうなるはずです。 そうならない場合、それはコンポーネントをさらに分解できることを意味します。 今回はUserControlA、UserControl1、UserControl2、UserControl3が それぞれ親の参照を持てば十分に用は足りると思います。 親は当然全ての参照を持つことになります。
そうです。 CallでもGoSubでも何でも。
よく推敲せずに適当に書いたら意味不明でした。 たいした意味はありません。 コンポーネントを作ったら、 内部の状態変化を通知するためのイベントを念のため作っておきますよ、 というだけです。 まぁこういったものは個々のスタイルですので、 絶対的に正しいものはありません。 参考になればと思い書いてみましたが… 正直私はこの辺の設計で他人のコードや説明が参考になったことはありません。 いろいろ試してレシピを増やせばそのうち悩まずに作れるようになるかと思います。 | ||||||||||||||||||||||||
|
投稿日時: 2008-05-16 16:07
ありがとうございます。 絶対に正しいものはないですが、参考にはなっています。 そう言えばWndProcを使っても見たことあります WndProcなら制御ぽくなって良いのかもしれませんが。。。。 | ||||||||||||||||||||||||
|
投稿日時: 2008-05-16 16:40
もう誰かが書いているかもしれませんが。。。 これは「ちゃんと Doc/View(あるいは MVC)すればよいパターン」だと思います。 UserControl にデータをカプセルするのではなく、Doc 役のクラスを作って、データはそこに集約することにします。 UserControl はどれも等しく、Doc 役のクラスが公開するメソッドを呼び出すことでデータを操作します。 UserControl は Model クラスが公開するイベントに接続して、データの更新を検知し、再描画等を行います。 | ||||||||||||||||||||||||
|
投稿日時: 2008-05-16 17:30
ありがとうございます。 モデル駆動と言う事ですね。 昔やった時もそんな感じで作ったのですが、 更新イベントの順番が登録順になるので (複雑な画面の場合ですが) 多重にイベントが発生したような。。。。 (特にChangedとかSelectedとかFocuse関係とか) (書きながら思ったのですがプライオリティを付ければ いいのかもしれませんが。。。) | ||||||||||||||||||||||||
|
投稿日時: 2008-05-16 17:33
「全体として一つ」っていう考えはあんまりよくないと思います。
ずっとその構造であることをどうやって保障しますか? こういうのは案外変わりやすいものです。 コンポーネント全体で一つのコンポーネントだからといって その構成要素は紛れもなくモジュール(ここでいうクラス)であって、 それぞれはできる限り疎結合になっているべきだと思います。 そのクラスがインナークラスにできるくらい小さなものであれば 循環参照もありかもしれません。(修正のコストが低いので) 問題なのはイベントのコストですが、 まずパフォーマンスはここでは考えるべきでないと思いますし 試してませんが問題になるレベルじゃないと思います。 コーディングについて考えてみると私はあまり差がないと思いました。 今後、通知の種類が増えたり通知する側が変更されたりするときに それに応じた構造に変えればいいと思います。 今、1対1の関係でかつ通知の種類が一つなので イベントではなくそのままデリゲートだけ保持するのもありかもしれません。
これは私もほとんど使いません。 今回の例でも使わないと思います。
今回の例のような大きいクラス同士が循環依存になるとお互いの修正に敏感になるので あまりいい構造とは思いません。
あえて泥沼に足を突っ込むことは・・・。 Control.Invoke、Control.BeginInvokeで十分だと思います。
それでできるところはそれがいいですね。 View内のクラス間で更新があるのでそれも問題だと思いますが。 | ||||||||||||||||||||||||
|
投稿日時: 2008-05-16 17:55
「子 View も親 View と同じ Doc を参照する」という手もあるし、「子 View は親 View がカプセルする、子 View 用の Doc を参照する」という手もあります。 | ||||||||||||||||||||||||
|
投稿日時: 2008-05-16 19:37
うーん?? UserControlAの選択が変更されたときの通知のタイミングが問題だと思ったんですが これもそれで解決できるんですか? | ||||||||||||||||||||||||
|
投稿日時: 2008-05-16 20:22
UserControlA (View) は Model を知っている。(参照を持っている。) UserControlA…ツリーでしたっけ? これの選択行が変更されるというのは Model が変更されるということ、つまり、UserControlA が Model.SelectedItem とか何かを変更すればいい。 Model の変更にともなって、SelectionChanged のようなイベントを送出する。それを契機に、それぞれの View が自分の表示を更新したらよい。 |