GameMaker 日本語掲示板

【プログラミング一般】監視するか報告するか?

9 コメント
views
16 フォロー

GMS限定の話じゃないのですが、プログラミングについて質問です。
わかりやすいタイトルが考えつかなかったので意味不明なタイトルですみません。

画像1

図のような仕組みを作る時、いつもどうするのが正解なのかな?と思っています。みなさんならどういう作りにしますか?

Aが毎フレームBを監視するのは、A側にコードを集約できるからメンテ性が上がる気がしますが、監視の負荷が気になります。
BからAに処理完了を伝える方法は伝達1回で住むので負荷は無いですが、コードがあちこちにバラけるのでメンテ性が下がる気がします。

普段こう考えてこうやってる、というのがあれば教えて下さい。

asa
作成: 2021/03/09 (火) 14:12:12
通報 ...
1

GMSではないですが、自分はAがBを監視する構造にすることが多いです
理由は、Bがなんらかの理由で消失した場合に備えたり、全体の状況が変わった場合にBを停止させたりのように、どうせA側になんらかの管理の処理が入ってしまうので、それならBは「処理をやる。終わったら止まる」だけで完結したモジュールであるほうが汎用性が高まる気がするからです
またAがBを複数所持することになった場合、監視型ではAにBのリストを持って全部監視するだけですみますが、通知型にするとAがすべてのBの通知をプールして判定するという煩雑な処理が結局必要になってしまうので…

6

考え方の説明も含めて参考になります。ありがとうございます!

2
生高橋 2021/03/09 (火) 15:02:25

メンテ性を重視で、AがBのやつを監視するほうですね。
自分は負荷とかあんまり気にしてないでとりあえず動け精神でやってます。

7

無視できる負荷を気にするあまり、仕組みが手に負えない複雑さになるのも良くないですからね。
ありがとうございます!

3

「ポーリング(監視)型」vs「コールバック型」の問題ですかね

今回の例では「監視したい人(A)」と「監視される人(B)」が1対1の単純な関係性なので「AがBを毎フレーム監視する」という実装が一番シンプルになりそうですね。

Aの数が動的に変化するような場合は「コールバック型」を使います。その際「オブザーバーパターン」を適用することが多いです。

今回挙げられた例で避けたい設計は「Bが終了したことをAに伝える時、BがAの関数を呼び出す」という形です。この形にすると「BはAに依存する」ことになり「BはAなしには存在できない」ことになります。

自分が設計を行う際に最も気をつけていることの1つが「依存関係をシンプルにする」ということで、これを設計の指針にしていますね。仮に全体の実装が複雑になってしまう場合でも「依存関係をシンプルにする」ことを常に心がけています。

5

ポーリング型、コールバック型、オブザーバーパターン、いろいろ勉強になります。
仮にBが終了したことをAに伝える形にする場合、どういう方法にすると独立性を保てるのでしょうか?

8

>仮にBが終了したことをAに伝える形にする場合、どういう方法にすると独立性を保てるのでしょうか?

「Bが終了したら呼び出される関数」をBに登録できるようにして、Bは終了時に登録されている関数を呼び出す、という形で実現できます。Bは誰に何の関数を登録されたのか知りません。終了時にただ機械的に登録された関数を呼び出すだけです。この設計にすることでBはAの存在を知ることなく終了したことを知りたい人に伝えることが出来ます。

C/C++では関数ポインタやファンクタ、C#ではデリゲート等で実装できます。
(GMSは触ったことがないので実現方法が分かりません、、、ごめんなさい、、、)

私が最初のポストで挙げた「コールバック型」というのはまさに上記のような実装のことです。特定のタイミングで呼び出してもらいたい関数を登録する設計です。

9
asa 2021/03/10 (水) 00:40:35 修正 >> 8

GMSは関数への参照idを変数に入れたり、それを他の関数に渡したり渡された関数を実行したりできるので、それが該当するようです(GMSでの呼び方はよく分かりません)。

例えば、
Aが「歩く」のあとでBに演出表示をさせて「止まって」待機し、Bが完了したタイミングで「ジャンプ」に移る場合は、Aの「ジャンプする」の部分を関数jumpとして切り出しておいて、AがBに指示を出す時点で関数jumpを渡す(=コールバック関数)。Bは自分の処理が終わったら渡されてるA関数jumpを実行。その結果、Aはジャンプすると。

// AからBに指示
with (B) { scr_effect_show(id, scr_jump); }

イメージ的にはAはBに「ジャンプ」の動きを移譲し、ぼーっと待機。Bは自分の処理を終えて関数jumpを呼び出し。Aは遠隔で操られ動かされる感じでしょうか(全然違うかもしれませんが)。

関数jumpがどうやってAを参照し動かすのかですが、たぶん上記のコードのように、Aがコールバック関数を渡す時に自分のidを一緒に渡しておけば下記のようにBはwith構文で実行できるのでこれで対処できそうです。

// Bの終了時
with (someone) { script_execute(scr_jump); }

これでBはAが誰だろうと知ったこっちゃなく何をするのかも知ったこっちゃなく済みます(と思います…)

教えていただいたキーワードでいろいろ調べてみようと思います。ありがとうございます。

4
asa 2021/03/09 (火) 18:45:32 修正

皆さんありがとうございます。
理由、考え方も含めてとても勉強になります!

自分が作ってるものに当てはめると、AがBを監視する形がいいようです。
ここで聞いてよかったなー。