読者です 読者をやめる 読者になる 読者になる

WEB系エンジニアの勉強日記

Baby steps to Giant strides!

モデリング練習 Kata01: Supermarket Pricing

codekata.com

スーパーマッケートの価格設定、購入周りのモデリングの練習。

要件

  • 下記のような複雑な価格設定をどう実現させるか?

    • 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つ無料だとすると、会計時,商品全スキャン後に対象の割引セットがあるかを判定し、あったら割引を行う。

会計時の処理
  • 商品をスキャン
  • 割引セット対象があるかを判定
  • 全商品価格 - 割引セットの価格を表示
  • レシートの出力 ( 全商品 * 個数, 割引セット内容 )
在庫管理

シンプルに商品の個数、又はグラム数を管理する。

データ構造

関係性をざっくりと画像にしてみた。 f:id:sissoko:20150821202519p:plain

実装

codekata/01 at master · kitabatake/codekata · GitHub

考察

要件を整理して、要件に必要なデータ構造、関係性をしっかり定義してから実装に移る。

実装方法が頭に浮かぶと、それをすぐに実装したくなりがちなので注意する。