最初はチープな画面を作ってロジックを確認を

今更ながら年始の雑文で穴埋めを | 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 という簡単な組み合わせにしています。このボタンイベントのところが長くなったら、適宜ロジックのほうへ移行ってな雰囲気で組み立てるとよいかと。

カテゴリー: 雑談 パーマリンク