今更ながら年始の雑文で穴埋めを | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3978
の続き。
ロジックの確認のために、下記のようなチープな画面を作成する。
この手のロジックをチェックするのは、UnitTest を使うのがいいのですが、今回は UI のチェックも兼ねるので、Windows フォームを使っています。最初は、いきなり Windows ストア アプリにしようかと思ったのですが、UI のコーディングがバインドと混ざってややこしくなるので、フォームアプリで作成ということで。
フォームに書いたコードは使い捨てになるので、できるだけ簡潔に…と云いますか、フォームでもWindows ストア アプリでもそれなりに動くようにしたいので(コードレベルでは無理なので、ロジックの手続きレベルで)、ロジックの呼び出しテストも兼ねて。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | 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 という簡単な組み合わせにしています。このボタンイベントのところが長くなったら、適宜ロジックのほうへ移行ってな雰囲気で組み立てるとよいかと。