# Theatrical players refactoring Kata

### Objectives

* Learn how to refactor “legacy” code
* Practice OOP Design Patterns
* Practice FP concepts

### How to

Clone this repository : <https://github.com/ythirion/Theatrical-Players-Refactoring-Kata>

### Exercise

Add an HTML output with the same information

## Facilitation

### What do you think about this code ?

{% code title="StatementPrinter.java" %}

```java
import java.text.NumberFormat;
import java.util.Locale;
import java.util.Map;

public class StatementPrinter {

    public String print(Invoice invoice, Map<String, Play> plays) {
        var totalAmount = 0;
        var volumeCredits = 0;
        var result = String.format("Statement for %s\n", invoice.customer);

        NumberFormat frmt = NumberFormat.getCurrencyInstance(Locale.US);

        for (var perf : invoice.performances) {
            var play = plays.get(perf.playID);
            var thisAmount = 0;

            switch (play.type) {
                case "tragedy":
                    thisAmount = 40000;
                    if (perf.audience > 30) {
                        thisAmount += 1000 * (perf.audience - 30);
                    }
                    break;
                case "comedy":
                    thisAmount = 30000;
                    if (perf.audience > 20) {
                        thisAmount += 10000 + 500 * (perf.audience - 20);
                    }
                    thisAmount += 300 * perf.audience;
                    break;
                default:
                    throw new Error("unknown type: ${play.type}");
            }

            // add volume credits
            volumeCredits += Math.max(perf.audience - 30, 0);
            // add extra credit for every ten comedy attendees
            if ("comedy".equals(play.type)) volumeCredits += Math.floor(perf.audience / 5);

            // print line for this order
            result += String.format("  %s: %s (%s seats)\n", play.name, frmt.format(thisAmount / 100), perf.audience);
            totalAmount += thisAmount;
        }
        result += String.format("Amount owed is %s\n", frmt.format(totalAmount / 100));
        result += String.format("You earned %s credits\n", volumeCredits);
        return result;
    }

}
```

{% endcode %}

* Will it be easy to do the exercise ?
* Why ?

![](https://1936518372-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAffO8xa1ZWmgZvfeK2%2F-MAjkVZxB9FhpfOZ8VpZ%2F-MAjlxxiBnBBhmbcCCE4%2Fimage.png?alt=media\&token=3ba2829e-ef68-4c4b-9dec-08b780c2685b)

### Identify code smells

![](https://1936518372-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAffO8xa1ZWmgZvfeK2%2F-MAjkVZxB9FhpfOZ8VpZ%2F-MAjmeOnnl9f3nTIM9Hy%2Fimage.png?alt=media\&token=5170e576-2093-46bb-a185-8b942108dd4d)

![](https://1936518372-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAffO8xa1ZWmgZvfeK2%2F-MAjkVZxB9FhpfOZ8VpZ%2F-MAjmL4yCwFnlzDPkbq-%2Fimage.png?alt=media\&token=0ae7b5a4-67b2-4b0e-904b-a4e390f381bb)

### Does it break any S.O.L.I.D principles ?

![](https://1936518372-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAffO8xa1ZWmgZvfeK2%2F-MAjkVZxB9FhpfOZ8VpZ%2F-MAjn-crAGMk4UxOIix8%2Fimage.png?alt=media\&token=30a7e7b3-41bd-493d-85cf-1111883b347e)

## How to start ?

{% hint style="info" %}
*"Before you start refactoring, make sure you have a solid suite of tests. Theses tests must be self-checking."* - Martin Fowler
{% endhint %}

### Which kind of tests can we do ?

The code already exists and works :

* Easiest way to add a regression test is to find some test data, exercise the code, and approve the result
* Add [approval tests](https://approvaltests.com/) / [snapshot tests](https://jestjs.io/docs/en/snapshot-testing) / [Golden master](https://codurance.com/2012/11/11/testing-legacy-code-with-golden-master/)

### Approval tests : generate output / create your golden master

![](https://1936518372-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAffO8xa1ZWmgZvfeK2%2F-MAjkVZxB9FhpfOZ8VpZ%2F-MAjoPioalxdSV35_C9J%2Fimage.png?alt=media\&token=959edad0-25a7-4922-8af8-478578fcc179)

We store this golden master in a file :

{% code title="StatementPrinterTests.exampleStatement.approved.txt" %}

```
Statement for BigCo
  Hamlet: $650.00 (55 seats)
  As You Like It: $580.00 (35 seats)
  Othello: $500.00 (40 seats)
Amount owed is $1,730.00
You earned 47 credits
```

{% endcode %}

### Approval tests : create a test

To do so we can use the library "[approvaltests](https://approvaltests.com/)".

The verify is an approvaltests method that will compare the result returned by the print method and our Golden master.

{% code title="StatementPrinterTests.java" %}

```java
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.Map;

import static org.approvaltests.Approvals.verify;

public class StatementPrinterTests {

    @Test
    void exampleStatement() {
        Map<String, Play> plays = Map.of(
                "hamlet",  new Play("Hamlet", "tragedy"),
                "as-like", new Play("As You Like It", "comedy"),
                "othello", new Play("Othello", "tragedy"));

        Invoice invoice = new Invoice("BigCo", List.of(
                new Performance("hamlet", 55),
                new Performance("as-like", 35),
                new Performance("othello", 40)));

        StatementPrinter statementPrinter = new StatementPrinter();
        var result = statementPrinter.print(invoice, plays);

        verify(result);
    }
```

{% endcode %}

### Have we missed something ?

We should ask ourselves if we have covered every piece of code with our test.&#x20;

To do so we have a tool : ***code coverage***.

![](https://1936518372-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAffO8xa1ZWmgZvfeK2%2F-MAjkVZxB9FhpfOZ8VpZ%2F-MAkJ82U3anrFFnKbL5v%2Fimage.png?alt=media\&token=5b3782dd-baac-4cf7-a1cd-c021c0bb72a0)

The Code Coverage (from IntelliJ here) shows us that the default case is not covered at the moment.

![Code coverage](https://1936518372-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAffO8xa1ZWmgZvfeK2%2F-MAjkVZxB9FhpfOZ8VpZ%2F-MAkJHCr_rCO5rJ_gYDI%2Fimage.png?alt=media\&token=f789cc0d-9357-4e17-90ba-05c7a10ebc9f)

So let's add a new test :

{% code title="StatementPrinterTests.java" %}

```java
        @Test
        void statementWithNewPlayTypes() {
            Map<String, Play> plays = Map.of(
                    "henry-v",  new Play("Henry V", "history"),
                    "as-like", new Play("As You Like It", "pastoral"));
        
            Invoice invoice = new Invoice("BigCo", List.of(
                    new Performance("henry-v", 53),
                    new Performance("as-like", 55)));
        
            StatementPrinter statementPrinter = new StatementPrinter();
            Assertions.assertThrows(Error.class, () -> {
                statementPrinter.print(invoice, plays);
            });
        }
```

{% endcode %}

### Are we confident enough in our tests ?

To check this we can check the quality of our tests by using a concept called mutation testing.

{% content-ref url="../testing/mutation-testing" %}
[mutation-testing](https://yoan-thirion.gitbook.io/knowledge-base/software-craftsmanship/testing/mutation-testing)
{% endcontent-ref %}

![](https://1936518372-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAffO8xa1ZWmgZvfeK2%2F-MAjkVZxB9FhpfOZ8VpZ%2F-MAkKdwlWGdp8u_H-jKq%2Fimage.png?alt=media\&token=1911fd17-aef8-4a8f-a443-b5c9485174be)

You can use tools like pitest (for Java) or stryker (for C#, Javascript, Scala).

Now we are confident enough, let's do the exercise.

## 2 ways of refactoring

![](https://1936518372-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAffO8xa1ZWmgZvfeK2%2F-MAjkVZxB9FhpfOZ8VpZ%2F-MAkL1HvqrFHbE54Oe3k%2Fimage.png?alt=media\&token=8697bae1-e563-4ddf-b19e-f891f4844db8)

{% content-ref url="theatrical-players-refactoring-kata/lets-refactor-oop-style" %}
[lets-refactor-oop-style](https://yoan-thirion.gitbook.io/knowledge-base/software-craftsmanship/code-katas/theatrical-players-refactoring-kata/lets-refactor-oop-style)
{% endcontent-ref %}

{% content-ref url="theatrical-players-refactoring-kata/lets-refactor-fp-style" %}
[lets-refactor-fp-style](https://yoan-thirion.gitbook.io/knowledge-base/software-craftsmanship/code-katas/theatrical-players-refactoring-kata/lets-refactor-fp-style)
{% endcontent-ref %}

## To go further

* Base artcile from Emily Bache : [https://www.praqma.com/stories/refactoring-kata/  ](https://www.praqma.com/stories/refactoring-kata/)
* Approval Tests : [https://approvaltests.com/  ](https://approvaltests.com/)
* Strategy pattern : [https://refactoring.guru/design-patterns/strategy  ](https://refactoring.guru/design-patterns/strategy)
* Facilitator slide deck :

{% embed url="<https://speakerdeck.com/thirion/theatrical-players-refactoring-kata>" %}
