デバッグ地獄

アバター
せんちゃ
記事: 50
登録日時: 15年前
住所: 江別市東野幌町
連絡を取る:

デバッグ地獄

投稿記事 by せんちゃ » 12年前

仕事のほうもデバッグ期間に突入。
テスターさんからの報告が多ければ多いほど苦労しますが、今のところそこまでではない
とはいえ、これから地獄が待っています。

一応私の担当部分はほぼすべて終了しており、
残作業もそこまで重いものではないので今回の件の仕事はほぼ終了なのかなと感じます。
もう過去に実装した部分はバグ修正に入らなければいけないので迂闊に設計部分の手直しができそうにないですし。

なのでここらで今回学習したことをまとめたいなぁと思っています。




学習その1  アプリとシステムは分けて考える

アプリは上の部分、システムは下の部分。
特定のプラットフォームの機能は下の部分でまとめてモジュール化してしまう。
アプリ部分にプラットフォームの機能を持ち込むとハードウェアの依存性が強くなり大変なことになります。
下の部分こそ汎用的に作れど、上はなるべく決まった機能を提供するようにしたほうが良い。



学習その2  グローバル変数を使うならスコープは限定する。もしくは窓口をつくる

基本的にグローバルな変数の使用は望ましくないものの、
ゲーム全体で利用するドキュメントなどは静的になりがちです。
シングルトンにすればメンテしやすいのかというとそんなことはなく、シングルトンであろうがグローバル変数であろうが
根本的な解決にはなりません。

使うならば直接データを参照するのではなく、窓口を作るべきです。


学習その3  有効値は決める

有効じゃない値ならちゃんとログでエラーを吐かせたり、それ用のハンドリングをするように防御意識を大事にです。
前回の日記からアサートは使うべきか否かという話題にもなったのですが、
そこはケースバイケースでいいかなという結論に至りました。
リリース版だとプリプロセッサでアサートは消えるので、
なるべく失敗したときは派手にバグらせるような方法をとるのも一つの手なのかもしれません。
たとえばリソースの読み込みに失敗したなら描画命令は無視する、とかならビジュアルではっきりとわかります。



学習その4  演技部分と計算ロジックは分けて考える
視覚的な演出と計算は分けて作るべきです。
ネットワークなら計算はすべてサーバー処理、クライアントはサーバーからの結果の値から
「自分が今どうあるべきか」を判断して各自で演技するように作ります。


学習その5  一方的に投げるのか、相手から受け取って挙動するのか、を使い分ける。

今回の仕事で初めて覚えた言葉として「プッシュ型」と「プル型」があります。
プッシュ型は一方的な命令、
プル型は誰かの持っている状態を見て各自がするべき演技をおこなう、
といったニュアンスです。
プッシュ型である場合は一回命令をしたら受けた側は勝手にやってくれるようにします。
そのあとの命令を「した側」と「された側」で、終了の同期などはとると大変難しいつくりになってしまうので、
命令したからまぁあとは勝手によろしく!って感じだとシンプルです。


学習その6  オブジェクトマネージャーを作るならマネージャーしか参照を握ってはいけない
リソースにもいえることですが、マネージャーを作るならオブジェクト情報はマネージャーしか知らないように作ります。
オブジェクトの情報をマネージャーは知っていますが、そのポインタ、インスタンスを渡すようなことはしてはいけません。
各オブジェクトはユニークな管理番号を持ち、マネージャーにその管理番号を投げて「こういう行動をしてくれ!」
とマネージャー側でやってしまえば、インスタンスの有効チェックもマネージャーだけで済むので見通しが良くなります。



学習その7  プロセスの親子関係を意識する

2DのUIを含める、ゲーム画面に存在する物体は必ず親が存在するので
なるべく個々のオブジェクト同士が参照しあったり命令しあったりするのではなく、親を通して命令させるべきです。


学習その8  トリガーを意識する。
前回の情報と今の情報から、一致しない場合はその瞬間は状態変化トリガーといえます。
ステート管理をする場合はこの変化タイミングを見て、各ステートの最初の処理をいれてあげるようにします。




学習その9  呼ばれるタイミングに依存しないつくりにする

「この関数の次にこの関数をよぶとおかしいことになる」みたいなのはアプリ部分の実装では許すべきではありません(システムはこの限りではない)
どれがどういう状態であっても個々に動けるように意識です。




学習その10 オブジェクト指向を意識するならセッタやゲッタも消極的にするべき

参照口はなるべく少なくするべきです。
ゲッターとセッターがペアで存在しているのなら、
値の書き換えができる状態になるのでその時点でカプセル化は成立しません.
必要以上にデータセットはできないようにしたいものです。
(とはいってもパラメーターとかだと難しい)


まだまだ学習したことはあるんだけどひとまずこんな感じです
今日はちょっと眠いのでまた次回にでもあばばばばば

アバター
海Sea
記事: 102
登録日時: 14年前

Re: デバッグ地獄

投稿記事 by 海Sea » 12年前

凄いですね。
ただ学習10で、
オブジェクト指向のカプセル化は、
privateな変数の値を、
直接外部から参照や変更したりできないようにすること(つまりpublicな変数を使わない)で、
setter,getterは必要な時に用意して使うと思ってましたね。
アクセサメソッドを使用できるようにするとカプセル化が成立しないというのが、
何でだろうと疑問に思ってしまいました。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前

Re: デバッグ地獄

投稿記事 by softya(ソフト屋) » 12年前

10は、なぜ外部に公開しないと出来ないかを再確認しろって事では無いでしょうか?
ゲッターとセッターがペアで存在しているとなると、単なるデータ置き場?な感じがします。
オブジェクトの構成自体やら処理に問題がある可能性が高いと言うことでしょうね。

アバター
海Sea
記事: 102
登録日時: 14年前

Re: デバッグ地獄

投稿記事 by 海Sea » 12年前

softya(ソフト屋) さんが書きました:10は、なぜ外部に公開しないと出来ないかを再確認しろって事では無いでしょうか?
ゲッターとセッターがペアで存在しているとなると、単なるデータ置き場?な感じがします。
オブジェクトの構成自体やら処理に問題がある可能性が高いと言うことでしょうね。
設計としてペアでゲッターとセッターがあると
おかしいということなんでしょうかね?
ちょっとそこらへんが僕には認識不足でよくわかりづらいのですが
隠蔽化って単純に別々のプログラマーが同じプログラムを組むときに
間違って不必要な変数などを使用することを減らすためのものだと
ずっとそう思ってました(^^;

CODE:


Class A
{
   public int b=0;
   private String c="test";
}

class B
{
   A a = new A()

   a.b = 5;//これはpublicなので可能
   a.c = "clear";//privateなため不可能
}

最後に編集したユーザー 海Sea on 2013年4月13日(土) 16:34 [ 編集 1 回目 ]

ISLe
記事: 2650
登録日時: 15年前

Re: デバッグ地獄

投稿記事 by ISLe » 12年前

オブジェクト指向としては、振る舞いとして意味のあるメソッドを用意しろってことだと思います。

CODE:

// x,yに移動

// こういうことをされると困る
hoge.setX(x);
hoge.setY(y);

hoge.moveTo(x,y);
最後に編集したユーザー ISLe on 2013年4月13日(土) 16:48 [ 編集 1 回目 ]

アバター
へにっくす
記事: 634
登録日時: 13年前

Re: デバッグ地獄

投稿記事 by へにっくす » 12年前

テストまでいきましたか。
地獄へようこそw
テストするにもいろいろ学べることは多くあるはずなので、頑張ってくださいね

4と5はMVCフレームワークに通じるところがあるかな。Mがビジネスモデル(計算ロジック)、Vがビュー(演技)、Cがコントローラ(イベント発生など)。
10の件は、
getter/setterを頻繁に使うぐらいなら単なる構造体として使用すべきだってことかな。
ISLeさんもコメントしてる通り、意味が分からない処理は極力避けた方がよいです。

YuO
記事: 947
登録日時: 14年前

RE: デバッグ地獄

投稿記事 by YuO » 12年前

せんちゃ さんが書きました:ゲッターとセッターがペアで存在しているのなら、
値の書き換えができる状態になるのでその時点でカプセル化は成立しません.
「getterとsetterがペアで存在している」と「値の書き換えができる状態になる」と「カプセル化が成立しない」,
どれも関係ないと思いますが……。

まず,getterとsetterがペアで存在している場合に,その背景で何をしているのでしょうか。
もしかしたら,単一のメンバ変数への読み書きをしているだけかもれしませんし,複数のメンバ変数への読み書きをしているのかもしれません。

さらに,getterとsetterのペアは内部の実装を晒しているわけではないので,カプセル化にも関係しません。

また,setterを正しく実装すれば,クラス内部の状態を無矛盾にできます。
# 例えば,日付型に月を設定するset_month関数があって,日が30日を指している時のset_month(2);は例外を発生させる等。
setterの役割の一つは,クラス内部の状態を無矛盾に保つことです。
これも,カプセル化を保つために必要なことです。


getter/setterを用意するのがよくないのは,メンバ変数をpublicにするのがよくないからと,機械的に置き換えているような場合です。
あくまでpublicインターフェースに必要なgetter/setterを排除する必要はありませんし,それがカプセル化を壊すようなことはありません。

アバター
海Sea
記事: 102
登録日時: 14年前

Re: デバッグ地獄

投稿記事 by 海Sea » 12年前

ISLe さんが書きました:オブジェクト指向としては、振る舞いとして意味のあるメソッドを用意しろってことだと思います。

CODE:

// x,yに移動

// こういうことをされると困る
hoge.setX(x);
hoge.setY(y);

hoge.moveTo(x,y);
そういうことですか。
設計として、意味のないメソッドは確かにいらないですね。

アバター
せんちゃ
記事: 50
登録日時: 15年前
住所: 江別市東野幌町
連絡を取る:

Re: デバッグ地獄

投稿記事 by せんちゃ » 12年前

>YuOさん
>海Sea
すこし言い方が雑でしたが、私が書きたかったのは安易に値を変えられる窓口は作らないということです。

># 例えば,日付型に月を設定するset_month関数があって,日が30日を指している時のset_month(2);は例外を発生させる等。
>setterの役割の一つは,クラス内部の状態を無矛盾に保つことです。
>これも,カプセル化を保つために必要なことです。

というのは私の間違っていると考えているsetとは大きく概念が異なります。
汎用性を意識して設計したモジュールはそれでいいと思いますが(むしろそれがそのモジュールの仕事なので)

例えば、ゲームでプレイヤーが内部でstateという変数で状態を持っているとき、
setState( DESTROY )とした場合、どこでもプレイヤーを殺すことができてしまいます。
これはstateがDESTROYのときの演技をしてしまうからですが、
このようにどこからでも値を変更できることでオブジェクトの挙動が変わってしまう可能性が生まれるのはデータのカプセル化が出来ていないと思います。

また、あるオブジェクトがあるオブジェクトにある値をあるデータにセットする、ということが下の処理でたくさん存在していては
オブジェクト間の依存関係が高まり、大きい視点から眺めるとやはりカプセル化できているとは言い難いと思います。

getterはまだ見るだけなので問題はさほど大きくありませんが、
setterに関して言えば慎重に扱わなければ深刻なバグを作る原因だと思います。

つまるところ、
ゲームオブジェクトはデータを安易に書き換えられるようには作らない
と書いたほうが伝わりやすかったのかもしれません。
最後に編集したユーザー せんちゃ on 2013年4月14日(日) 00:28 [ 編集 1 回目 ]

ISLe
記事: 2650
登録日時: 15年前

Re: デバッグ地獄

投稿記事 by ISLe » 12年前

せんちゃ さんが書きました:つまるところ、
ゲームオブジェクトはデータを安易に書き換えられるようには作らない
と書いたほうが伝わりやすかったのかもしれません。
その5やその9とかぶるところがありますかね。
ダングリングの問題とかありますし、「外部から直接状態を変更すべきではない」ということですね。