# 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>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yoan-thirion.gitbook.io/knowledge-base/software-craftsmanship/code-katas/theatrical-players-refactoring-kata.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
