モデリング練習 Kata01: Supermarket Pricing
スーパーマッケートの価格設定、購入周りのモデリングの練習。
要件
下記のような複雑な価格設定をどう実現させるか?
- 3つで1000円
- 100g 200円
- 2つ買ったら1つ無料
会計時の処理、在庫管理はどうやって行うか?
価格の設定について
個数単位での価格設定と、重量単位での価格設定をどう実現させるか? price_per_amount, amount, unit の項目が必要か?
- トマト 1 (amount) 個 (unit) 100円 (price_per_amount)
- 豚肉 100 (amount) g (unit) 200円 (price_per_amount) のような。
丸め込みなどの仕様は置いておいて、下記のような形で実現できそう。
def calculate_price (num) num / @amount * @price_per_amount end
商品セットについて
- 1個300円だけど、3つ買えば1000円。
- 2つ買ったら1つ無料。 のような要件にどう対応するか?
商品 (Product) と同じように振舞うが、商品を複数持つことができる商品セット (ProductSet) で実現できるか?
class ProductSet has_and_belongs_to_many :products def calculate_price (num) num * @price end def output_receipt (num) buff = "" products.each do |p| buff << p.output_receipt end end end
- product_product_sets
- product_id
- product_set_id
- product_amount
会計時の処理について
必要とされるもの - スキャン (全商品の個数、又はg) - 「2つ買ったら1つ無料 」のようなセット対象があるかどうかの判定, 反映 - 記録 - レシートの出力
記録するもの
- 全商品個数、又はg
- 対象セット
実装イメージ
class Account has_many :account_products, :account_product_set end
- account_products
- account_id
- product_id
- amount
上記ようなイメージで、価格計算とレシート出力時には、商品ストックからProductSetのフィルタリングを行った後に、商品ストックに対して, calculate_price, output_receipt を行う.
失敗1 実装方法を優先してしまった
上記のイメージで実装してみたが、
- 3つで1000円
- 100g 200円
- 2つ買ったら1つ無料
の要件は満たせたが、どうもスッキリしない実装になってしまった。
原因としては、本サイトに上げられている要件例に囚われてしまい、その実装方法(Product, ProductSet)を思いついたことで、すぐに実装に取り掛かってしまった。
それによって、商品の価格の管理や、会計時にどういったデータを記録に残すかなどの仕様を詰めきれていなかったので、最初に決めた実装に引っ張られる形になって、気持ち悪い実装になってしまった。
どうすれば良かったか?
要件例はあくまで例で、もっと具体的な要件に落とし込む必要があった ?
商品管理や、会計、在庫管理での要件を整理して、実際にどういったデータ構造が良いかをしっかり決めてから実装に移った方がよかった。
大事なのは具体的な要件とそれを満たすデータ構造で、実装方法はそれほど重要じゃない?
前者2件がしっかりきまれば自然と決まってくるものではないか?
要件の際整理
もう一度要件を整理する。
商品管理
商品の売り上げ、在庫を個数で管理するものと、重量で管理するものがある。 重量の単位はグラムとする。
トマト3つで200円のような商品は,「トマト3つ」という商品で管理をする。
割引セット
2つ買ったら1つ無料のような対応は「割引セット(DiscountSet)」という考えで対応する。 靴下2使ったら1つ無料だとすると、会計時,商品全スキャン後に対象の割引セットがあるかを判定し、あったら割引を行う。
会計時の処理
- 商品をスキャン
- 割引セット対象があるかを判定
- 全商品価格 - 割引セットの価格を表示
- レシートの出力 ( 全商品 * 個数, 割引セット内容 )
在庫管理
シンプルに商品の個数、又はグラム数を管理する。
データ構造
関係性をざっくりと画像にしてみた。
実装
codekata/01 at master · kitabatake/codekata · GitHub
考察
要件を整理して、要件に必要なデータ構造、関係性をしっかり定義してから実装に移る。
実装方法が頭に浮かぶと、それをすぐに実装したくなりがちなので注意する。