コントローラ vs モデル
どちらにどの機能を持たせるかはいつでも悩みの種だ。
ドキュメントにもあるように、小さいアプリならビューとコントローラが一体の場合も多いし、この前の棒取りゲームなんてMVC、三位一体状態だった。
どう振り分けるかが設計者の腕の見せ所。まずはチンパンジーゲームに必要な機能のリストアップだ。
1)横hマス x 縦vマスの基盤に、1~nまでの数字をどのマス目に置いていくか決定する。
2)上記マス目配置情報を画面上に再現する。
3)ユーザーが画面上の1~nまでの数字のうちのどれを押したかを判別する。
4)1~nの順にマス目が押せているか判断する。
5)判断結果を表示する。
このうち、2)、3)はビューとコントローラでないと請け負えない。先に説明したテキストコントロールなら2)は
コントローラ:どのビューにどの数字を入れるか判断し設定。
ビュー:設定された数字を表示
とさらに機能を分割されるだろうし、3)も同様に
ビュー:自分が押されたことをコントローラに通知
コントローラ:どのビューが押されたかを判断し数字とマス目を判断。
となるだろう。
4)については1)で1~nまでの数字のマス目情報をもらえるのだからコントローラでも判断できる。じゃあコントローラに入れてしまうかというと、コントローラはビューに合わせたさまざまなタイプのクラスが発生する可能性がある。そのクラスごとに判断処理を組み込むよりは、ビューの変化に引きずられないモデル側にもたせて判断させる方が適切やね。
5)は、どのようなビューを使い、どのように表示させるかを決定するのがコントローラ、実際に画面上に表示するのがビューということになる。
こうやって、眺めてもビューとコントロールはかなり一体になる要素が多いことがわかる。実際、チンパンジーゲーム程度の規模なら、ビューにコントロールの機能も組み込んでも何の不都合もない。ビューとコントロールにわける効果はもう少し大規模なアプリケーションでないと発揮されないんだけど、ま、小さいアプリでやると見通しいいし、勉強になるのであえてMVCでやります。
ということで、モデルの機能は以下に決定。
1)横hマス x 縦vマスの基盤に、1~nまでの数字をどのマス目に置いていくか決定する。
4)1~nの順にマス目が押せているか判断する。
名前はチンパンジーゲームの出題者、判定者、試験管あたりから
ChimpanzeeExaminer
各機能のメソッド名は
1)横hマス x 縦vマスの基盤に、1~nまでの数字をどのマス目に置いていくか決定する。
startGame
4)1~nの順にマス目が押せているか判断する。
judge
で、受け渡されるデータ構造は
typedef struct {
int h;
int v;
} SNumberLocation;
この構造体の配列で順序を表現とする。NSArrayにした方が安全面に優れるけど、まずは配列で実装。
judgeではstartGameが呼ばれた時点で1番目の数字が入るマス目位置が正解となり、呼び出されるたびに次の番号の数字が入るマス目位置が正解となることとする。
となれば、各メソッドの引数は
void startGame:(int)countOfNumbers (SNumberLocation*)locations
countOfNumbers
今回のゲームで出題する番号の数、1~countOfNumbers個までの数字のランダムにマス目上に配置することになる。
locations
SNumberLocation構造体の配列でこの要素順が対応する数字となる(ただしC言語では配列は0から始める規則なので、表示するときは0番目の要素 -> 1、1番目の要素 -> 2というふうにしなければならない)。
また、配列は呼び出す側が用意し必ずcountOfNumbers分の要素を持つ配列でなければならない。
BOOL judge:(SNumberLocation)location
location
押された番号のマス目位置。startGame後の最初の呼び出しでは1番目のマス目位置、次の呼び出しでは2番目のマス目位置を渡してやらなければ返り値がTRUEとならない。この判定する番号を記録するためint curtNumberを用意する。また判定するためにstartGameで設定したlocations情報も必要となる。
ということでinterfaceはこんな感じだな。
@interface ChimpanzeeExaminer :NSObject {
SNumberLocation* locations;
int curtNumber;
}
- void startGame:(int)countOfNumbers (SNumberLocation*)locations;
- BOOL judge:(SNumberLocation)location;
@end
次回はimplementation。
どちらにどの機能を持たせるかはいつでも悩みの種だ。
ドキュメントにもあるように、小さいアプリならビューとコントローラが一体の場合も多いし、この前の棒取りゲームなんてMVC、三位一体状態だった。
どう振り分けるかが設計者の腕の見せ所。まずはチンパンジーゲームに必要な機能のリストアップだ。
1)横hマス x 縦vマスの基盤に、1~nまでの数字をどのマス目に置いていくか決定する。
2)上記マス目配置情報を画面上に再現する。
3)ユーザーが画面上の1~nまでの数字のうちのどれを押したかを判別する。
4)1~nの順にマス目が押せているか判断する。
5)判断結果を表示する。
このうち、2)、3)はビューとコントローラでないと請け負えない。先に説明したテキストコントロールなら2)は
コントローラ:どのビューにどの数字を入れるか判断し設定。
ビュー:設定された数字を表示
とさらに機能を分割されるだろうし、3)も同様に
ビュー:自分が押されたことをコントローラに通知
コントローラ:どのビューが押されたかを判断し数字とマス目を判断。
となるだろう。
4)については1)で1~nまでの数字のマス目情報をもらえるのだからコントローラでも判断できる。じゃあコントローラに入れてしまうかというと、コントローラはビューに合わせたさまざまなタイプのクラスが発生する可能性がある。そのクラスごとに判断処理を組み込むよりは、ビューの変化に引きずられないモデル側にもたせて判断させる方が適切やね。
5)は、どのようなビューを使い、どのように表示させるかを決定するのがコントローラ、実際に画面上に表示するのがビューということになる。
こうやって、眺めてもビューとコントロールはかなり一体になる要素が多いことがわかる。実際、チンパンジーゲーム程度の規模なら、ビューにコントロールの機能も組み込んでも何の不都合もない。ビューとコントロールにわける効果はもう少し大規模なアプリケーションでないと発揮されないんだけど、ま、小さいアプリでやると見通しいいし、勉強になるのであえてMVCでやります。
ということで、モデルの機能は以下に決定。
1)横hマス x 縦vマスの基盤に、1~nまでの数字をどのマス目に置いていくか決定する。
4)1~nの順にマス目が押せているか判断する。
名前はチンパンジーゲームの出題者、判定者、試験管あたりから
ChimpanzeeExaminer
各機能のメソッド名は
1)横hマス x 縦vマスの基盤に、1~nまでの数字をどのマス目に置いていくか決定する。
startGame
4)1~nの順にマス目が押せているか判断する。
judge
で、受け渡されるデータ構造は
typedef struct {
int h;
int v;
} SNumberLocation;
この構造体の配列で順序を表現とする。NSArrayにした方が安全面に優れるけど、まずは配列で実装。
judgeではstartGameが呼ばれた時点で1番目の数字が入るマス目位置が正解となり、呼び出されるたびに次の番号の数字が入るマス目位置が正解となることとする。
となれば、各メソッドの引数は
void startGame:(int)countOfNumbers (SNumberLocation*)locations
countOfNumbers
今回のゲームで出題する番号の数、1~countOfNumbers個までの数字のランダムにマス目上に配置することになる。
locations
SNumberLocation構造体の配列でこの要素順が対応する数字となる(ただしC言語では配列は0から始める規則なので、表示するときは0番目の要素 -> 1、1番目の要素 -> 2というふうにしなければならない)。
また、配列は呼び出す側が用意し必ずcountOfNumbers分の要素を持つ配列でなければならない。
BOOL judge:(SNumberLocation)location
location
押された番号のマス目位置。startGame後の最初の呼び出しでは1番目のマス目位置、次の呼び出しでは2番目のマス目位置を渡してやらなければ返り値がTRUEとならない。この判定する番号を記録するためint curtNumberを用意する。また判定するためにstartGameで設定したlocations情報も必要となる。
ということでinterfaceはこんな感じだな。
@interface ChimpanzeeExaminer :NSObject {
SNumberLocation* locations;
int curtNumber;
}
- void startGame:(int)countOfNumbers (SNumberLocation*)locations;
- BOOL judge:(SNumberLocation)location;
@end
次回はimplementation。