今更ながら年始の雑文で穴埋めを | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3978
の続き。
ロジックの確認のために、下記のようなチープな画面を作成する。
この手のロジックをチェックするのは、UnitTest を使うのがいいのですが、今回は UI のチェックも兼ねるので、Windows フォームを使っています。最初は、いきなり Windows ストア アプリにしようかと思ったのですが、UI のコーディングがバインドと混ざってややこしくなるので、フォームアプリで作成ということで。
フォームに書いたコードは使い捨てになるので、できるだけ簡潔に…と云いますか、フォームでもWindows ストア アプリでもそれなりに動くようにしたいので(コードレベルでは無理なので、ロジックの手続きレベルで)、ロジックの呼び出しテストも兼ねて。
public partial class Form1 : Form { public Form1() { InitializeComponent(); _board.Player1.EventSelCard += Player1_EventSelCard; listBox1.Sorted = true; listBox2.Sorted = true; listBox3.Sorted = true; listBox4.Sorted = true; listBox5.Sorted = true; } /// <summary> /// 2枚から選択する /// </summary> /// <param name="c1"></param> /// <param name="c2"></param> /// <returns></returns> Card Player1_EventSelCard(Card c1, Card c2) { string msg = string.Format( "{0} と {1} があります。{2} を選択しますか?", c1.ID, c2.ID, c1.ID); var btn = MessageBox.Show(msg, "", MessageBoxButtons.YesNo); if (btn == System.Windows.Forms.DialogResult.Yes) { return c1; } else { return c2; } } GameBoard _board = new GameBoard(); Player _curPlayer; /// <summary> /// 山から1枚取得 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { Card c = _board.Yama.GetCard(); _board.Ba.PutCard(c); // 画面の更新 ScrUpdate(); } /// <summary> /// マッチしたカードを場から取得 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click(object sender, EventArgs e) { var lst = _board.Game.MatchBa(_board.Ba, _board.Ba.PlayerCard, _curPlayer); _board.Ba.GetCard(lst); _curPlayer.GetCard(lst); // 画面の更新 ScrUpdate(); } /// <summary> /// 花札を配る /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button5_Click(object sender, EventArgs e) { // 花札を配る _board.Reset(); _curPlayer = _board.Player1; // 画面の更新 ScrUpdate(); } /// <summary> /// player1のカードを場に出す /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button3_Click(object sender, EventArgs e) { if (listBox2.SelectedIndex == -1) return; var c = (Card)listBox2.SelectedItem; _board.Player1.IntoBa(c); _board.Ba.PutCard(c); _curPlayer = _board.Player1; // 画面の更新 ScrUpdate(); } /// <summary> /// player2のカードを場に出す /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button4_Click(object sender, EventArgs e) { if (listBox4.SelectedIndex == -1) return; var c = (Card)listBox4.SelectedItem; _board.Player2.IntoBa(c); _board.Ba.PutCard(c); _curPlayer = _board.Player2; // 画面の更新 ScrUpdate(); } /// <summary> /// 画面の更新 /// </summary> void ScrUpdate() { listBox1.Items.Clear(); listBox2.Items.Clear(); listBox3.Items.Clear(); listBox4.Items.Clear(); listBox5.Items.Clear(); listBox1.Items.AddRange(_board.Ba.Cards.ToArray()); listBox2.Items.AddRange(_board.Player1.MyCards.ToArray()); listBox3.Items.AddRange(_board.Player1.TakenCards.ToArray()); listBox4.Items.AddRange(_board.Player2.MyCards.ToArray()); listBox5.Items.AddRange(_board.Player2.TakenCards.ToArray()); label7.Text = _board.Ba.PlayerCard.ToString(); } }
画面のロジックは基本はユーザーからのイベントドリブンで、ユーザーがなんらかの選択をした時に発生します。昔は業務ロジックと画面からのイベントを分離させるために、「イベントごとに関数を作る」ってなことをやってたりしますが、煩雑なので、最近はパスですね。ですが、NUnit などの自動テストを有効に働かせるためには、画面のほうに業務ロジックを入れないようにするのがベター…なのですが、いまのところこれといった定番がありません。
業務アプリのような簡単な(?)画面の場合には、MVVM か MVC で分離させて、業務ロジックを NUnit でテストして、くっつけた上でアプリ上で再テストって手順がよいのですが、ゲームの場合はどうなんでしょうね?ってのが、今の私の(解消しなければいけない)疑問点です。
そんな訳で、ユーザーがアクションをするボタンの類と、画面更新 ScrUpdate という簡単な組み合わせにしています。このボタンイベントのところが長くなったら、適宜ロジックのほうへ移行ってな雰囲気で組み立てるとよいかと。