簡易ORMフレームワークを作成してみる(15)

ハンバーガショップの料金計算

作成したORMの検索処理を少し複雑なモデルで実践で利用できるか確認します。シナリオは、以前作成したハンバーガショップのモデルの料金計算です。このシナリオでは関連データが階層構造になっているなどそれなりに複雑になっています。

商品ドメインモデル

まずは、商品のドメインモデルを作成するために、商品と関連したテーブルで構成される型指定されたDataSetを作成します。

次に、型指定されたDataSetにドメインモデルとして利用するためのビジネスロジックを追加していきます。今回は型指定されたDataSetで定義されたクラスを拡張する方法でドメインモデルを作成しています。POCOを利用する方法もありますが、今回のビジネスロジックレベルであれば本方式でも十分対応できます。

今回シナリオは商品の料金計算を行うものなので、商品Rowに単価取得メソッドを追加すればOKです。ただ、ハンバーガショップの料金計算では、オプションの構成で料金が変化したり、構成要素が誤っている場合の例外処理を考慮する必要があるなど、なかなかタフな点もあります。しかし、実際に実装してみると、ドメインモデルのメリットでしょうか、データアクセスなどの余計なことを気にする必要がなくビジネスロジックの実装に集中できるので案外簡単に実装できてしまいます。型指定されたDataSetのプロパティアクセスが少し分かりにくいかもしれませんが、商品Rowに追加されたメソッド・プロパティを見てもらえれば感じはわかると思います。

partial class 商品Row
{
	public decimal 単価取得(List<商品Row> optionList)
	{
		decimal optionPrice = 0;
		if (optionList != null)
		{
			追加可能オプション商品Spec spec = new 追加可能オプション商品Spec(this);
			foreach (商品Row option in optionList)
			{
				if (!spec.IsSatisfiedBy(option))
				{
					throw new ApplicationException(
					  option.商品名 + "は不正です");
				}
				spec.AddOptionList(option);
				optionPrice += this.追加価格取得(option);
			}
			if (!spec.HasAllItem)
			{
				throw new ApplicationException("構成要素が不足しています");
			}
		}
		return this.単体価格 + optionPrice;

	}

	public string アイテム名取得(商品Row option)
	{
		単体商品_構成商品Row item = FindOption(option);
		if (item == null) return null;
		return item.アイテム名;
	}

	public List アイテムリスト
	{
		get 
		{
			List itemList = new List();
			foreach (単体商品_構成商品Row row in this.Get単体商品_構成商品Rows())
			{
				if (!itemList.Contains(row.アイテム名))
					itemList.Add(row.アイテム名);
			}
			return itemList;
		}
	}

	public decimal 追加価格取得(商品Row option)
	{
		単体商品_構成商品Row item = FindOption(option);
		if (item == null) return 0;
		return item.追加価格;
	}

	private 単体商品_構成商品Row FindOption(商品Row option)
	{
		if (this.Get単体商品_構成商品Rows().Length == 0) return null;
		foreach (単体商品_構成商品Row row in this.Get単体商品_構成商品Rows())
		{
			if (row.構成商品ID == option.商品ID) return row;
			if (row.Get商品Rows()[0].Isグループ(option)) return row;
		}
		return null;
	}

	public bool Isグループ(商品Row item)
	{
		if (this.Getグループ商品構造Rows().Length == 0) return false;
		foreach (グループ商品構造Row groupRow in this.Getグループ商品構造Rows())
		{
			if (groupRow.アイテム商品ID == item.商品ID) return true;
			if (groupRow.Get商品Rows()[0].Isグループ(item)) return true;
		}
		return false;
	}
}

public class 追加可能オプション商品Spec
{
	private 商品Row _item;
	private List _addedOptionList;

	public 追加可能オプション商品Spec(商品Row item)
	{
		_item = item;
		_addedOptionList = new List();
	}

	public bool IsSatisfiedBy(商品Row option)
	{
		if (_item.アイテム名取得(option) == null) return false;
		foreach (string addedOption in _addedOptionList)
		{
			if (_item.アイテム名取得(option) == addedOption) return false;
		}
		return true;
	}

	public bool HasAllItem
	{
		get
		{
			foreach (string item in _item.アイテムリスト)
			{
				if (!_addedOptionList.Contains(item)) return false;
			}
			return true;
		}
	}

	public void AddOptionList(商品Row item)
	{
		_addedOptionList.Add(_item.アイテム名取得(item));
	}
}

partial class 商品DataSet
{
	public 商品Row FindBy商品名(string name)
	{
		foreach (商品Row item in this.商品)
		{
			if (item.商品名 == name) return item;
		}
		return null;
	}
}