サンプル ハンバーガショップ

概念モデル(クラス図)


台帳分析

各概念に対してインスタンス例を台帳として表現する。

商品クラスの台帳分析


台帳の一般化

サイズのバリエーションをなくすために、サイズがないものは「なし」にする。次に構成アイテムはセット商品だけでなく付属品も含めて商品もアイテムを全て扱うものとして位置づける。さらに空の場合も認めるようにすることで、以下のようになる。


クラス図への整理

台帳データからオブジェクトを識別しクラス構造に整理する。
台帳から、商品は単体価格のあるものとないものに分類、さらにグループの総称のものの3つに分類できる。


お買い上げ


店舗


論理データベース

商品のサブクラスを1つの商品テーブルでまとめる
販売商品とお買い上げ明細が1対1なのでまとめる


実装コード

商品

partial class 商品DataSet
{
    partial class 商品_構成商品Row
    {
        public 商品Row 構成商品
        {
            get
            {                    
                return this.Get商品Rows()[0];
            }
        }
    }
 
    partial class 商品_構成アイテムRow
    {
        public bool Is必須オプション商品
        {
            get
            {
                if (this.Get商品_構成商品Rows().Length == 1)
                {
                    return this.Get商品_構成商品Rows()[0].構成商品.タイプ == "単体";
                }
                return false;
            }
        }
        public 商品Row 必須オプション商品
        {
            get
            {
                if (Is必須オプション商品)
                {
                    return this.Get商品_構成商品Rows()[0].構成商品;
                }
                return null;
            }
        }
 
        public decimal 追加価格取得(商品Row option)
        {
            商品_構成商品Row item = Get構成商品(option);
            if (item == null) return 0;
            return item.追加価格;
        }
 
        internal 商品_構成商品Row Get構成商品(商品Row option)
        {
            foreach (商品_構成商品Row row in Get商品_構成商品Rows())
            {
                if (row.構成商品ID == option.商品ID) return row;
                if (row.構成商品.Isグループ(option)) return row;
            }
            return null;
        }
 
        public bool Is構成可能(商品Row option)
        {
            return Get構成商品(option) != null;
        }
 
        internal List<商品Row> Listオプション商品()
        {
            List<商品Row> retList = new List<商品Row>();
            foreach (商品_構成商品Row row in Get商品_構成商品Rows())
            {
                retList.Add(row.構成商品);
            }
            return retList;
 
        }
        
    }
 
    partial class 商品Row
    {
        internal 商品_構成アイテムRow Get構成アイテム(string itemName)
        {
            if (Get商品_構成アイテムRows().Length ==0) return null;
            foreach (商品_構成アイテムRow row in Get商品_構成アイテムRows())
            {
                if (row.アイテム名 == itemName) return row;
            }
            return null;
        }
 
        public bool Is構成可能アイテム(string itemName, 商品Row option)
        {
            商品_構成アイテムRow item = Get構成アイテム(itemName);
            return item.Is構成可能(option);
        }
 
        public List List必須オプション商品アイテム()
        {
            List retList = new List();
            foreach (商品_構成アイテムRow row in this.Get商品_構成アイテムRows())
            {
                if (row.Is必須オプション商品)
                {
                    retList.Add(row.アイテム名);
                }
            }
            return retList;
        }
 
        public List<商品Row> Listオプション商品(string itemName)
        {
            商品_構成アイテムRow row = Get構成アイテム(itemName);
            return row.Listオプション商品();
        }
 
        public List アイテムリスト名
        {
            get
            {
                List itemList = new List();
                foreach (商品_構成アイテムRow row in this.Get商品_構成アイテムRows())
                {
                    itemList.Add(row.アイテム名);
                }
                return itemList;
            }
        }
 
        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 string アイテムリスト列
        {
            get
            {
                return string.Join(",",アイテムリスト名.ToArray());
            }
        }
 
    }
 
    public 商品Row FindBy商品ID(string id)
    {
        return this.商品.FindBy商品ID(id);
    }
 
 
    public 商品Row FindBy商品名(string name)
    {
        foreach (商品Row item in this.商品)
        {
            if (item.商品名 == name) return item;
        }
        return null;
    }
 
}
お買い上げ


partial class お買い上げDataSet
{
 
    public お買い上げDataSet(商品DataSet context) : this()
    {
 
        _商品Context = context;
    }
 
    private 商品DataSet _商品Context;
    public 商品DataSet 商品Context
    {
        get { return _商品Context; }
    }
 
 
    public お買い上げ お買い上げ作成(string storeId)
    {
        お買い上げ newRow = this.お買い上げ.Newお買い上げRow();
        newRow.店舗ID = storeId;
        this.お買い上げ.Addお買い上げRow(newRow);
        return newRow;
    }
 
    partial class お買い上げRow
    {
        private 商品DataSet 商品Context
        {
            get
            {
                お買い上げDataSet ds = (お買い上げDataSet)this.tableお買い上げ.DataSet;
                return ds.商品Context;
            }
        }
 
        public 税金 税金
        {
            get { return new 税金(); }
        }
 
 
        public お買い上げ明細Row 明細追加(商品 item, decimal num)
        {
            お買い上げDataSet ds = (お買い上げDataSet)this.tableお買い上げ.DataSet;
            お買い上げ明細 newRow = ds.お買い上げ明細.Newお買い上げ明細Row();
            newRow.お買い上げRow = this;
            newRow.商品ID = item.商品ID;
            newRow.数量 = num;
            newRow.価格 = item.単体価格;
 
            ds.お買い上げ明細.Addお買い上げ明細Row(newRow);
            foreach (string itemName in item.List必須オプション商品アイテム())
            {
                List<商品> optList =item.Listオプション商品(itemName);
                newRow.オプション追加(itemName, optList[0]);
            }
 
            this.Update合計金額();
            return newRow;
        }
 
        public void 確定()
        {
            お買い上げ明細Validation spec = new お買い上げ明細Validation();
            foreach (お買い上げ明細 row in this.Getお買い上げ明細Rows())
            {
                if (!spec.Validate(row)) 
                    throw new ApplicationException(
string.Format("確定 {0} {1}", spec.ErrorCode, row.お買い上げ商品.商品名));
            }
 
            Update合計金額();
            this.日時 = DateTime.Now;
        }
 
        internal void Update合計金額()
        {
            decimal amount = 0;
            foreach (お買い上げ明細 row in this.Getお買い上げ明細Rows())
            {
                amount += row.価格 * row.数量;
            }
            this.税抜き合計金額 = amount;
            this.税 = 税金.消費税計算(amount);
            this.税込み合計金額 = amount + this.税;
        }
 
    }
 
 
    partial class お買い上げ明細Row
    {
        private 商品DataSet 商品Context
        {
            get
            {
                お買い上げDataSet ds = (お買い上げDataSet)this.tableお買い上げ明細.DataSet;
                return ds.商品Context;
            }
        }
 
        public 商品 お買い上げ商品
        {
            get 
            {
                return 商品Context.FindBy商品ID(this.商品ID);
            }
        }
 
        public 販売商品明細 オプション追加(string itemName, 商品 option)
        {
            お買い上げDataSet ds = (お買い上げDataSet)this.tableお買い上げ明細.DataSet;
            販売商品明細 newRow = ds.table販売商品明細.New販売商品明細Row();
            newRow.お買い上げ明細Row = this;
            newRow.商品ID = option.商品ID;
            newRow.アイテム名 = itemName;
 
            販売商品明細Validation spec = new 販売商品明細Validation();
            if (!spec.Validate(newRow)) throw new ApplicationException(
string.Format("オプション追加エラー {0},{1},{2}", spec.ErrorCode, itemName, option.商品名));
 
            ds.table販売商品明細.Add販売商品明細Row(newRow);
            Update価格();
            this.お買い上げRow.Update合計金額();
            return newRow;
        }
 
        private void Update価格()
        {
                decimal amount = this.お買い上げ商品.単体価格;
                foreach (販売商品明細 option in Get販売商品明細Rows())
                {
                    amount += お買い上げ商品.Get構成アイテム(option.アイテム名).追加価格取得(option.オプション商品);
                }
                this.価格 = amount;
        }
 
    }
 
    partial class 販売商品明細Row
    {
        private 商品DataSet 商品Context
        {
            get
            {
                お買い上げDataSet ds = (お買い上げDataSet)this.table販売商品明細.DataSet;
                return ds.商品Context;
            }
        }
 
        public 商品 オプション商品
        {
            get
            {
                return 商品Context.FindBy商品ID(this.商品ID);
            }
        }
    }
    
    public class 販売商品明細Validation : DomainValidation<販売商品明細>
    {
 
        public override bool Validate(販売商品明細 option)
        {
            商品 item = option.お買い上げ明細Row.お買い上げ商品;
            if (!item.Is構成可能アイテム(option.アイテム名, option.オプション商品))
            {
                ErrorCode = "ErrorOptionInvalid";
                return false;
            }
 
            if (option.お買い上げ明細Row.Get販売商品明細Rows().Length == 0) return true;
 
            foreach (販売商品明細 detail in option.お買い上げ明細Row.Get販売商品明細Rows())
            {
                if (detail.アイテム名 == option.アイテム名)
                {
                    ErrorCode = "ErrorDetailDuplicate";
                    return false;
                }
            }
            return true;
        }
    }
 
 
    public class お買い上げ明細Validation : DomainValidation<お買い上げ明細>
    {
       
        public override bool Validate(お買い上げ明細 detail)
        {
            商品 item = detail.お買い上げ商品;
 
            if (item.Get商品_構成アイテムRows().Length !=
                detail.Get販売商品明細Rows().Length)
            {
                ErrorCode = "ErrorDetailShort";
                return false;
            }
            foreach (string masterItem in item.アイテムリスト名)
            {
                bool match = false;
                foreach (販売商品明細 option in detail.Get販売商品明細Rows())
                {
                    if (masterItem == option.アイテム名)
                    {
                        if (match)
                        {
                            ErrorCode = "ErrorDetailDuplicate";
                            return false;
                        }
                        match = true;
                    }
                }
                if (!match)
                {
                    ErrorCode = "ErrorDetailShort";
                    return false;
                }
            }
            return true;
        }
    }
 
}
店舗