Code Kata 9

Back to the supermarket. This week, we’ll implement the code for a checkout system that handles pricing schemes such as apples cost 50 cents, three apples cost \$ 1.30. Way back in KataOne we thought about how to model the various options for supermarket pricing. We looked at things such as “three for a dollar,\$1.99 per pound,” and “buy two, get one free.” This week, let’s implement the code for a supermarket checkout that calculates the total price of a number of items. In a normal supermarket, things are identified using Stock Keeping Units, or SKUs. In our store, we’ll use individual letters of the alphabet (A, B, C, and so on). Our goods are priced individually. In addition, some items are multipriced: buy n of them, and they’ll cost you y cents. For example, item A might cost 50 cents individually, but this week we have a special offer: buy three ‘A’s and they’ll cost you \$1.30. In fact this week’s prices are:

Our checkout accepts items in any order, so that if we scan a B, an A, and another B, we’ll recognize the two B’s and price them at 45 (for a total price so far of 95). Because the pricing changes frequently, we need to be able to pass in a set of pricing rules each time we start handling a checkout transaction.

Check out process = No Abstraction

> prices <- data.frame(items = LETTERS, price = sample.int(100,
     26, replace = T), qty = sample.int(10, 26, replace = T),
     discount = 0)
> prices$discount <- round(with(prices, qty * price * (1 - runif(26))))
> print(prices)
   items price qty discount
1      A    26   7       44
2      B    90   7      154
3      C    41   5      201
4      D    90  10      313
5      E    93   9      121
6      F    96   3      240
7      G    70   6      134
8      H    55   3       15
9      I    15   1        2
10     J    14   9       87
11     K    79   8      542
12     L    67   7      107
13     M     4   8       11
14     N    15   5        7
15     O    53   6      168
16     P    18   1       15
17     Q    68   5      140
18     R    31   2       25
19     S     8   2       15
20     T    55   5      261
21     U    42  10      115
22     V    70   9      547
23     W    13   9      107
24     X    24   9       21
25     Y    36   2       26
26     Z    51   2       86
> n <- 100
> cart <- sample(LETTERS, n, replace = T)
> process <- table(cart)
> process <- data.frame(bought = names(process), qty.bought = c(process))
> process <- merge(process, prices, by.x = "bought", by.y = "items",
     all.x = TRUE)
> process$cost <- with(process, discount * (qty.bought%/%qty) +
     price * (qty.bought%%qty))
> print(process)
   bought qty.bought price qty discount cost
1       A          3    26   7       44   78
2       B          2    90   7      154  180
3       C          3    41   5      201  123
4       D          5    90  10      313  450
5       E          7    93   9      121  651
6       F          5    96   3      240  432
7       G          3    70   6      134  210
8       H          4    55   3       15   70
9       I          5    15   1        2   10
10      J          2    14   9       87   28
11      K          6    79   8      542  474
12      L          4    67   7      107  268
13      M          4     4   8       11   16
14      N          6    15   5        7   22
15      O          5    53   6      168  265
16      P          2    18   1       15   30
17      Q          4    68   5      140  272
18      R          1    31   2       25   31
19      S          2     8   2       15   15
20      T          7    55   5      261  371
21      U          3    42  10      115  126
22      V          4    70   9      547  280
23      W          3    13   9      107   39
24      X          2    24   9       21   48
25      Y          1    36   2       26   36
26      Z          7    51   2       86  309
> cat(" The total cost is ", sum(process$cost), "\n")
 The total cost is  4834

Now let me replicate the exact data given in the codekata

> prices <- data.frame(items = LETTERS[1:4], price = c(50, 30,
     20, 15), qty = c(3, 2, 1, 1), discount = c(130, 45, 20, 15))
> print(prices)
  items price qty discount
1     A    50   3      130
2     B    30   2       45
3     C    20   1       20
4     D    15   1       15
> test.items <- c("", "A", "AB", "CDBA", "AA", "AAA", "AAAA", "AAAAA",
     "AAAAAA", "AAAB", "AAABB", "AAABBD", "DABABA")
> test.res <- c(0, 50, 80, 115, 100, 130, 180, 230, 260, 160, 175,
     190, 190)
> getCost <- function(cart) {
     if (nchar(cart) == 0) {
         return(0)
     }
     cart <- strsplit(cart, "")[[1]]
     process <- table(cart)
     process <- data.frame(bought = names(process), qty.bought = c(process))
     process <- merge(process, prices, by.x = "bought", by.y = "items",
         all.x = TRUE)
     process$cost <- with(process, discount * (qty.bought%/%qty) +
         price * (qty.bought%%qty))
     return(sum(process$cost))
 }
> res <- cbind(sapply(test.items, getCost))
> for (i in seq_along(res)) {
     expect_that(res[i], equals(test.res[i]))
 }
> testIncremental <- function(x) {
     cart <<- paste(cart, x, sep = "")
     return(getCost(cart))
 }
> cart <- ""
> expect_that(testIncremental(""), equals(0))
> expect_that(testIncremental("A"), equals(50))
> expect_that(testIncremental("B"), equals(80))
> expect_that(testIncremental("A"), equals(130))
> expect_that(testIncremental("A"), equals(160))
> expect_that(testIncremental("B"), equals(175))

I have implemented a very basic non abstracted version of the code. I need to look at design patterns. Actually I have forgotten all design patterns from Mark Joshi’s book. I need to go over the book some time. One of the comments says Decorator patttern is used. I have to understand what that is.

Here is a comment that has made me curious to go over this codekata more thoroughly

I finally uploaded a 20" screencast for the Checkout-Kata: http://www.vimeo.com/11604377 It was the result of a Kata session we did at the Jax-conference in germany this year. There are thousand TDD-aspects i’d still love to improve, but i like the decoupling of cart and rules in this implementation. Basically the cart allows the rules to select which items they want to bill. Nice side effect: the cart items (“A”, “B”,… in my case) can be replaced by any other object and the code should still work. I did this Kata about 20-25 times with 3 or 4 different approaches.

Learnings

  • Lessons from screencast and implementing the same in R
  • Need to revise Design Patterns