Skip to content

Commit

Permalink
Update notes
Browse files Browse the repository at this point in the history
  • Loading branch information
Albert Attard committed May 28, 2020
1 parent d848e99 commit 5aacf44
Show file tree
Hide file tree
Showing 4 changed files with 333 additions and 10 deletions.
25 changes: 15 additions & 10 deletions 04 - Classes, Methods and Objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -8648,28 +8648,33 @@ The value of a is: -126

**🚧 Pending...**

The word composition comes form Latin, *compositionem*, which means "to put together".
The word composition comes form Latin, [*compositio*](https://en.wiktionary.org/wiki/compositio), which means "*to put together*".



Consider the following example.
In software, composition is the ability of build some elaborate classes by putting together other classes. We have been using composition throughout the boot camp, without knowing. Take for example the following `Person` class.

```java
package demo;

public class Address {
public class Person {

private final String name;
private final int age;

private Country country;
private City city;
private PostalCode postalCode;
private Street street;
private Unit unit;
public Person( final String name, final int age ) { /* ... */ }

@Override
public String toString() { /* ... */ }
}
```

The `Person` **has a** `name` and **has an** `age`. The `Person` class is composed from a `String` and an `int`. Note that an emphasis was made on the **has a** phrase. In [inheritance](#inheritance), we use the phrase **is a**. For example, a `LightBox` **is a** `Box`. The following image shows the difference between inheritance and composition.

![Inheritance and composition](assets/images/Inheritance%20and%20composition.png)

### Why is there a big push in favour of composition over inheritance?



**🚧 Pending...**

### What are the disadvantages of composition?
Expand Down
Binary file modified assets/images/Images.pptx
Binary file not shown.
Binary file added assets/images/Inheritance and composition.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
318 changes: 318 additions & 0 deletions kata/Price Calculation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
# Price Calculation

## Target area

1. Inheritance and polymorphism
1. Arithmetic

## Story

A supermarket is growing in business and would like to automate the price calculation. Some prices are straightforward while others require some more thinking.

Consider the following examples:

1. Items are sold at a fix price per unit without any special offer or discounts.

For example, a can of beans is sold for `1.99 €`. Ten cans simply cost ten times the single unit price, `19.90 €`.

1. Weighted items are sold based on their weight.

For example, fruit is sold by its weight. `1Kg` of banana will cost twice as much as `500g` of the same banana.

1. Items are on special offer and the prices varies based on the number of items bought.

For example, the special offer maybe "*you buy 3 items and you pay for only 2*". If you buy four items, you will pay for three and if you buy six you pay for four, as shown in the following table.

| Quantity | Special offer applied | Price without special offer | Price with special offer applied |
|:--------:|:---------------------:|----------------------------:|---------------------------------:|
| `1` | NO | `1.10 €` | `1.10 €` |
| `2` | NO | `2.20 €` | `2.20 €` |
| `3` | **YES** (on 3) | `3.30 €` | (pay for 2 and 1 free) `2.20 €` |
| `4` | **YES** (on 3) | `4.40 €` | (pay for 3 and 1 free) `3.30 €` |
| `5` | **YES** (on 3) | `5.50 €` | (pay for 4 and 1 free) `4.40 €` |
| `6` | **YES** (on 6) | `6.60 €` | (pay for 4 and 2 free) `4.40 €` |

1. A discount is applied to all items bought if a certain amount is bought.

For example, a discount of `10%` is applied to all items when three or more items are bought. One can of olives costs `1.10 €`, while three cans will cost `2.97 €` (instead `3.30 €`).

| Quantity | Discount applied | Price without discount | Price with discount applied |
|:--------:|:----------------:|-----------------------:|-----------------------------:|
| `1` | NO | `1.10 €` | `1.10 €` |
| `2` | NO | `2.20 €` | `2.20 €` |
| `3` | **YES** | `3.30 €` | (`3.30 € - 0.33 €`) `2.97 €` |
| `4` | **YES** | `4.40 €` | (`4.40 € - 0.44 €`) `3.96 €` |

1. Discount applies to the next items if a certain amount is bought.

For example, a discount of `50%` is applied to the third and more items when three or more items are bought. For example, a shaving cream bottle costs `1.99 €`, while three bottles will cost `4.98 €` (instead of `5.97 €`).

| Quantity | Discount applied | Price without discount | Price with discount applied |
|:--------:|:----------------:|-----------------------:|------------------------------:|
| `1` | NO | `1.99 €` | `1.10 €` |
| `2` | NO | `3.98 €` | `3.98 €` |
| `3` | **YES** | `5.97 €` | (`5.97 € - 0.995 €`) `4.98 €` |
| `4` | **YES** | `7.96 €` | (`7.96 € - 1.99 €`) `5.97 €` |

For this first phase an item will never fall into more than one of the above categories. For example, fruit will either be a weighted item or discounted item, but never both.

All items bought will appear on the bill as line items. A line item comprises:

1. Name
1. Quantity (or weight depending on the type)
1. Unit (or kilo grams depending on the type) price
1. (optionally) Offer

A line item needs to:

1. Calculate the price, depending on its type and apply any applicable discounts or offers
1. Produce a description describing the item, quantity bought and the total price

Please note that the supermarket operates across the Euro-Zone. All monetary values should show the Euro symbol `` after the number, for example `1.99€`. Note that while Italy uses a `.` to separate the "*integer part*" from the "*fractional part*", Germany uses a `,`. This is governed by the [`Locale`](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/Locale.html).

## Acceptance Criteria

All line items, irrespective of the type, need to

1. Calculate the total price for a line item
1. Provide a description for a line item

### Calculate total price

| Line Item Type | Quantity/Weight | Unit Price | Discount/Special Offer | Total Price |
|:--------------------:|----------------:|-----------:|------------------------------------|------------:|
| Item | `1` | `1.99€` | Nothing (*qty × price*) | `1.99€` |
| Item | `5` | `1.99€` | Nothing (*qty × price*) | `9.95€` |
| Discounted item | `1` | `1.99€` | 10% Discount when buying 3 or more | `1.99€` |
| Discounted item | `2` | `1.99€` | 10% Discount when buying 3 or more | `3.98€` |
| Discounted item | `3` | `1.99€` | 10% Discount when buying 3 or more | `5.37€` |
| Special offer | `1` | `1.99€` | Buy 3 pay for 2 | `1.99€` |
| Special offer | `3` | `1.99€` | Buy 3 pay for 2 | `3.98€` |
| Special offer | `5` | `1.99€` | Buy 3 pay for 2 | `7.96€` |
| Weighted item | `0.987Kg` | `1.99€` | Nothing (*wgt × price*) | `1.96€` |
| Weighted item | `4.847Kg` | `1.99€` | Nothing (*wgt × price*) | `9.65€` |
| Discounted next item | `1` | `1.99€` | 50% on the third and more | `1.99€` |
| Discounted next item | `2` | `1.99€` | 50% on the third and more | `3.98€` |
| Discounted next item | `3` | `1.99€` | 50% on the third and more | `4.98€` |

### Provide a description


| Line Item Type | Name | Quantity | Unit Price | Description (`de`) | Description (`it_IT`) |
|----------------------|----------|----------:|-----------:|----------------------------------|----------------------------------|
| Item | `Sample` | `5` | `1.99€` | `Sample (5 × 1,99€) 9,95€` | `Sample (5 × 1.99€) 9.95€` |
| Weighted Item | `Sample` | `4.248Kg` | `1.99€` | `Sample (4,248Kg × 1,99€) 8,45€` | `Sample (4.248Kg × 1.99€) 8.45€` |

## Possible Solution

### 1

```java
package demo;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import java.math.BigDecimal;
import java.util.Locale;

import static org.junit.jupiter.api.Assertions.assertEquals;

@DisplayName( "Line item" )
public class LineItemTest {

@CsvSource( value = {
"5 | 0.50 | 2.50",
"2 | 1.99 | 3.98",
}, delimiter = '|' )
@DisplayName( "compute price" )
@ParameterizedTest( name = "should return {2}, for quantity {0} and price {1}" )
public void shouldComputePrice(
final int quantity,
final BigDecimal unitPrice,
final BigDecimal expectedComputedPrice
) {
final LineItem subject = new LineItem( "Test item", quantity, unitPrice );
assertEquals( 0, expectedComputedPrice.compareTo( subject.calculatePrice() ) );
}

@CsvSource( value = {
"Pack of spaghetti | 5 | 0.50 | de | Pack of spaghetti (5 × 0,50€) 2,50€",
"Pack of spaghetti | 5 | 0.50 | it_IT | Pack of spaghetti (5 × 0.50€) 2.50€",
"Can of beans | 2 | 1.99 | de | Can of beans (2 × 1,99€) 3,98€",
}, delimiter = '|' )
@DisplayName( "description" )
@ParameterizedTest( name = "should return {4}, for an item with name {0}, quantity {1} and price {2} in {3}" )
public void shouldDescribe(
final String name,
final int quantity,
final BigDecimal unitPrice,
final Locale locale,
final String expectedDescription
) {
final LineItem subject = new LineItem( name, quantity, unitPrice );
assertEquals( expectedDescription, subject.describe( locale ) );
}
}
```

```java
package demo;

import java.math.BigDecimal;
import java.util.Locale;

public class LineItem {

private final String name;
private final int quantity;
private final BigDecimal unitPrice;

public LineItem( final String name, final int quantity, final BigDecimal unitPrice ) {
this.name = name;
this.quantity = quantity;
this.unitPrice = unitPrice;
}

public BigDecimal calculatePrice() {
return unitPrice.multiply( new BigDecimal( quantity ) );
}

public String describe( final Locale locale ) {
return String.format( locale, "%s (%d × %.2f€) %.2f€", name, quantity, unitPrice, calculatePrice() );
}
}
```

### 2

```java
package demo;

import java.math.BigDecimal;
import java.util.Locale;

public abstract class BaseLineItem {

protected final String name;
protected final BigDecimal unitPrice;

protected BaseItem( final String name, final BigDecimal unitPrice ) {
this.name = name;
this.unitPrice = unitPrice;
}

public abstract BigDecimal calculatePrice();

public abstract String describe( final Locale locale );
}
```

```java
package demo;

import java.math.BigDecimal;
import java.util.Locale;

public class LineItem extends BaseLineItem {

private final int quantity;

public LineItem( final String name, final int quantity, final BigDecimal unitPrice ) {
super( name, unitPrice );
this.quantity = quantity;
}

@Override
public BigDecimal calculatePrice() {
return unitPrice.multiply( new BigDecimal( quantity ) );
}

@Override
public String describe( final Locale locale ) {
return String.format( locale, "%s (%d × %.2f€) %.2f€", name, quantity, unitPrice, calculatePrice() );
}
}
```

### 3

```java
package demo;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import java.math.BigDecimal;
import java.util.Locale;

import static org.junit.jupiter.api.Assertions.assertEquals;

@DisplayName( "Weighted line item" )
public class WeightedLineItemTest {

@CsvSource( value = {
"4.248 | 1.99 | 8.45352",
"0.987 | 1.99 | 1.96413",
}, delimiter = '|' )
@DisplayName( "compute price" )
@ParameterizedTest( name = "should return {2}, for weight {0} and price {1}" )
public void shouldComputePrice(
final BigDecimal weight,
final BigDecimal unitPrice,
final BigDecimal expectedComputedPrice
) {
final WeightedLineItem subject = new WeightedLineItem( "Test item", weight, unitPrice );
assertEquals( 0, expectedComputedPrice.compareTo( subject.calculatePrice() ) );
}

@CsvSource( value = {
"Banana | 4.248 | 1.99 | de | Banana (4,248Kg × 1,99€) 8,45€",
"Banana | 4.248 | 1.99 | it_IT | Banana (4.248Kg × 1.99€) 8.45€",
"Apple | 0.987 | 1.49 | de | Apple (0,987Kg × 1,49€) 1,47€",
}, delimiter = '|' )
@DisplayName( "description" )
@ParameterizedTest( name = "should return {4}, for an item with name {0}, quantity {1} and price {2} in {3}" )
public void shouldDescribe(
final String name,
final BigDecimal weight,
final BigDecimal unitPrice,
final Locale locale,
final String expectedDescription
) {
final WeightedLineItem subject = new WeightedLineItem( name, weight, unitPrice );
assertEquals( expectedDescription, subject.describe( locale ) );
}
}
```

```java
package demo;

import java.math.BigDecimal;
import java.util.Locale;

public class WeightedLineItem extends BaseLineItem {

private final BigDecimal weight;

protected WeightedLineItem( final String name, final BigDecimal weight, final BigDecimal unitPrice ) {
super( name, unitPrice );
this.weight = weight;
}

@Override
public BigDecimal calculatePrice() {
return weight.multiply( unitPrice );
}

@Override
public String describe( final Locale locale ) {
return String.format( locale, "%s (%.3fKg × %.2f€) %.2f€", name, weight, unitPrice, calculatePrice() );
}
}
```

### 4

0 comments on commit 5aacf44

Please sign in to comment.