patterncsharpMinor
Harry Potter books with discounts kata
Viewed 0 times
bookswithkataharrypotterdiscounts
Problem
I have implemented a solution to the Potter Kata. Basically, there are 5 different (types of) books, and the buyer gets a discount depending on how many different books he has bought. Multiple instances of the same book (type) don't get a discount.
The discount values are the following:
The difficulty in this kata comes from the jump in discount between 3 and 4 different books. I implemented a solution that - regarding the price algorithm - should be pretty easy to extend to more books and/or different discount values, but I fear that it's not very good code in general. That is, I think it's not easily extendable in itself, not in terms of the algorithm. One example for this is that I use a fixed number of book types (the array is initialized with
This is something that was created in the last couple of hours, so don't expect a lot of optimization of the algorithm or reduction of unnecessary calls (I don't like my
PriceCalc.cs
```
///
/// Library class to calculate the price of BookBasket objects.
///
public static class PriceCalc
{
public const decimal SingleBookPrice = 8m;
public const decimal PairDiscount = 0.95m;
public const decimal TripletDiscount = 0.9m;
public const decimal QuadrupletDiscount = 0.8m;
public const decimal QuintupletDiscount = 0.75m;
private static readonly ReadOnlyDictionary basicBasketsAndFactors = new ReadOnlyDictionary(
new Dictionary
{
{ new BookBasket(1), 1 },
{ new BookBasket(2), 1 },
{ new BookBasket(3), 1 },
{ new BookBask
The discount values are the following:
- 2 different books: 5%
- 3 different books: 10%
- 4 different books: 20%
- 5 different books: 25%
The difficulty in this kata comes from the jump in discount between 3 and 4 different books. I implemented a solution that - regarding the price algorithm - should be pretty easy to extend to more books and/or different discount values, but I fear that it's not very good code in general. That is, I think it's not easily extendable in itself, not in terms of the algorithm. One example for this is that I use a fixed number of book types (the array is initialized with
{0,0,0,0,0}), though that would be the part that is easiest to fix; or that I hard-coded the discounts. I'm sure you'll find lots of other stuff to criticize/comment on, and I greatly appreciate it, especially tips on how to make these classes easier to extend!This is something that was created in the last couple of hours, so don't expect a lot of optimization of the algorithm or reduction of unnecessary calls (I don't like my
IsFoo properties in that regard). It's more about extensibility and general class and method design.PriceCalc.cs
```
///
/// Library class to calculate the price of BookBasket objects.
///
public static class PriceCalc
{
public const decimal SingleBookPrice = 8m;
public const decimal PairDiscount = 0.95m;
public const decimal TripletDiscount = 0.9m;
public const decimal QuadrupletDiscount = 0.8m;
public const decimal QuintupletDiscount = 0.75m;
private static readonly ReadOnlyDictionary basicBasketsAndFactors = new ReadOnlyDictionary(
new Dictionary
{
{ new BookBasket(1), 1 },
{ new BookBasket(2), 1 },
{ new BookBasket(3), 1 },
{ new BookBask
Solution
Adding the possibility to extend this code can only be achieved by using a more OO approach.
This could be achieved by adding a
By adding a
Now we can store
Next we can replace the
We need to override the
For calculating the total price of the books contained in the
This could be achieved by adding a
Book class which has at least an Id, a Title, a Price and a DiscountId property where the DiscountId holds a reference to a DiscountInformation object. public class Book
{
public string Title { get; set; }
public int Id { get; private set; }
public double Price { get; private set; }
public int DiscountId { get; set; }
public Book(int id, double price, int discountId = int.MinValue)
: this(String.Empty, id, price, discountId)
{ }
public Book(string title, int id, double price, int discountId = int.MinValue)
{
Title = title;
Id = id;
Price = price;
DiscountId = discountId;
}
}By adding a
DiscountInformation class we can store the different discounts in relation to the number of different items. public class DiscountInformation
{
public int Id { get; private set; }
public Dictionary Discounts { get; private set; }
public DiscountInformation(int id)
{
Discounts = new Dictionary();
Id = id;
}
public DiscountInformation(int numberOfDifferentItems, double discount, int discountId)
:this(discountId)
{
Discounts.Add(numberOfDifferentItems, discount);
}
public void AddDiscount(int numberOfDifferentItems, double discount)
{
if (Discounts.ContainsKey(numberOfDifferentItems)) { return; }
Discounts.Add(numberOfDifferentItems, discount);
}
}Now we can store
DiscountInformation objects in a List, a Dictionary or a database for retrieval of the disounts for a list of books with the same value of DiscountId. Next we can replace the
private int[] storedBooks = new int[] { 0, 0, 0, 0, 0 }; of the BookBasket class with a List or better a Dictionary for storing a book and the amount of this book, which adds the possibility to remove the restriction of storing only up to 5 different books. We need to override the
Equals() and GetHashCode() methods in the Book class to use a Book object as the key of a Dictionary. For calculating the total price of the books contained in the
BookBasket we need to group the books by using the DiscountId property add the sum of the prices of the books and multiply the sum by the discount retrieved from the DiscountInformation by using the DiscountId and the amount of different books.Code Snippets
public class Book
{
public string Title { get; set; }
public int Id { get; private set; }
public double Price { get; private set; }
public int DiscountId { get; set; }
public Book(int id, double price, int discountId = int.MinValue)
: this(String.Empty, id, price, discountId)
{ }
public Book(string title, int id, double price, int discountId = int.MinValue)
{
Title = title;
Id = id;
Price = price;
DiscountId = discountId;
}
}public class DiscountInformation
{
public int Id { get; private set; }
public Dictionary<int, double> Discounts { get; private set; }
public DiscountInformation(int id)
{
Discounts = new Dictionary<int, double>();
Id = id;
}
public DiscountInformation(int numberOfDifferentItems, double discount, int discountId)
:this(discountId)
{
Discounts.Add(numberOfDifferentItems, discount);
}
public void AddDiscount(int numberOfDifferentItems, double discount)
{
if (Discounts.ContainsKey(numberOfDifferentItems)) { return; }
Discounts.Add(numberOfDifferentItems, discount);
}
}Context
StackExchange Code Review Q#82369, answer score: 3
Revisions (0)
No revisions yet.