Code Kata 1 – Supermarket Pricing
I’m going to attempt to go through all of the CodeKata exercices.
Why? I think it will help me to become a better developer, and I think it will be a lot of fun. (Note that the catalyst for this was Alan Stevens’s presentation on “Coding in Public“). I will
be recording brief overviews of my “solutions” here. Please feel free to suggest improvements or point out
weaknesses, because constructive criticism is, well, constructive.
Code Kata 1 is more of a thought exercise than a coding exercise. However, once I read through it,
I thought I would use some code to organize my thoughts better, and actually try to design some classes.
I struggled with the design for a while. The “can of beans” case is a trivial one, but I tried the “three for a dollar”
case and ran into a wall. It might sound equally simple, but I thought of Speedway, which sometimes
has Coke 2-Liters priced at 3/$3.33, but $1.59 if you buy less than 3. That really stumped me, until I realized
that the price depends on the total quantity purchased, which means I need some way to track the total
quantity purchased: Each item would contain a UPC code, and a quantity of that item.
The $1.99/pound case seemed pretty trivial: just store a price for weight, and then the weight. The Price()
method can do the math. I didn’t want to overthink this, so I didn’t.
Finally, the “buy two, get one free” case might sound tough, but it is trivial as well, since I
think the “three for a dollar” case at Speedway that I described pretty much covers that. So, I don’t even
need a new subclass for that.
Here is my complete class diagram:
I didn’t design a UI or anything, just some classes and tests. The source code is in C#, and I used MbUnit
for unit testing.
Here are the 5 other questions, which I didn’t really do any coding for:
1. Does fractional money exist?
Yes. Why not?
2. When (if ever) does rounding take place?
Well, since fractional money exists, we really don’t have to round. However, when paying with legal
tender, there is an actual smallest unit of payment that the consumer can use (a penny, for instance, or a Yen).
Prices needed to be rounded to that smallest unit. When the rounding takes place, I think, should be at
the last possible moment. However, that may depend on the grocery store, and if they have some rounding
logic they want to use to nickel & dime their customers. If it was me, I’d add up all the fractional
cents, and then round right at the very end.
3. How do you keep an audit trail of pricing decisions (and do you need to)?
In my code, the only thing to audit is the adding of items to the basket, and then the calculation of the
final price (itemized). In the real world, items would need to be able to be removed from the basket, coupons
added, etc. In that case, I would consider an itemized receipt to be the final audit: I don’t care how many
times you change your mind about buying that box of cereal, I just care about what you finally paid for, how
many you paid for, and how much you paid for it. So, my Basket could generate an entire receipt, not just
a Total scalar.
4. Are costs and prices the same class of thing?
I didn’t do any costing, but costs should be totally separate from price! If I buy 144 boxes of Raisin Bran
at $1.00 a box, that’s my cost no matter what I sell them at. Now, in a real supermarket, often times there
are “return” agreements, in which the vendor will take back unsold stock. There might be some logic to that,
and it would certainly vary on the vendor and agreement.
5. If a shelf of 100 cans is priced using “buy two, get one free”, how do you value the stock?
Again, my costs wouldn’t change based on the pricing or promotion. 3 boxes of Raisin Bran cost me $1.00 each, so
my stock is valued at $144. Real inventory systems may need to follow LIFO or FIFO rules, but I still don’t
see prices having any affect on that.
That concludes my first Kata. It took me about 4 nights of coding-pondering-starting over to come to
this solution. What could have been done better?