Knowledge-base
  • Home
  • Samman Technical Coaching
  • Software craftsmanship
    • Practices
      • Pair Programming
      • Code Review
      • Co-designs
      • Design sessions
      • Interview Domain Experts
      • Dev ethics
    • The Software Craftsman
    • Egoless Crafting
    • Technical debt workshop
    • Functional Programming made easy in C# with Language-ext
    • F# for OO programmers
    • Domain Modeling Made Functional
    • Testing
      • Clean Tests
      • Improve the design and testing of your micro-services through CDC Tests
        • CDC testing made simple with Pact
        • Pact broker : the missing piece of your Consumer-Driven Contract approach
      • Improve your test quality with Mutation testing
      • How to name our Unit Tests
      • How to write better assertions
    • Katas
      • TDD
        • Stack kata
        • Fizzbuzz
        • Outside-in TDD (London Style)
      • Improve your software quality with Property-Based Testing
        • A journey to Property-Based Testing
      • Clean Code
      • Clean Architecture
      • Write S.O.L.I.D code
      • Mocking
      • Gilded Rose (Approval Testing)
      • Mikado method
        • Mikado kata
      • Pure functions
      • Theatrical players refactoring Kata
        • Let's refactor (OOP style)
        • Let's refactor (FP style)
      • Functional Programming made easy in Java & C#
      • Refactoring journey
      • Refactoring du Bouchonnois
        • 1) Se faire une idée du code
        • 2) "Treat warnings as errors"
        • 3) Let's kill some mutants
        • 4) Améliorer la lisibilité des tests
        • 5) "Approve Everything"
        • 6) Définir des propriétés
        • 7) Tests d'architecture
        • 8) Use Cases
        • 9) Tell Don't Ask
        • 10) "Avoid Primitives" - Commands
        • 11) "Avoid Exceptions"
        • 12) "Event Sourcing"
    • Software Design X-Rays
      • Workshop
    • The Programmer's Brain
      • How to read code better
  • Software Architecture
    • Fundamentals of Software Architecture
    • Aligning Product & Software Design
    • DDD re-distilled
    • Test your architecture with Archunit
    • NoSQL
  • Agile coaching
    • How to run a Community of Practices (COP)
    • The developers — the forgotten of agility
      • The secrets to re-on-board the devs in agility
    • Coaching toolbox
      • Echelle
      • Learning expedition
    • How to improve Team Decision making ?
      • Decision Making Principles and Practices
    • Learning 3.0
    • Retrospectives
      • Back to the Future
      • Mission Impossible
      • Movie themes
      • Rétro dont vous êtes le héros
      • Sad/Mad/Glad
      • Speed boat
      • Star wars theme
      • Story cubes
    • Technical Agile Coaching with the Samman Method
    • Xanpan - a team centric agile method story
    • XTREM WATCH — Découvrez la puissance de la veille collective
    • Become a better speaker through peer feedback
    • Project-to-Product Principles
  • Leadership
    • Bref. J'ai pris une tarte dans la gueule (et ça fait extrêmement de bien)
    • Forward Summit 2020
    • Learn leadership from the Navy SEALs
    • Learn to lead and help your team(s) to be successful
    • Towards a learning organization and beyond
    • Leadership is language
  • Serious games
    • My serious games
    • Libérez vos entretiens d’embauche avec la gamification
    • How to create a game
    • How to debrief a game ?
    • Lego Serious Play (LSP)
      • LSP in your job interviews
  • Xtrem Reading
    • Cultivate Team Learning with Xtrem Reading
    • My Book Infographics
    • How to make book infographics
    • En route vers l’apprenance avec Xtrem Reading
    • Resources
      • Book notes
        • Agile People: A Radical Approach for HR & Managers
        • Agile testing : A Practical Guide for Testers and Agile Teams
        • Boite à outils de l'intelligence émotionnelle
        • Building a better business using Lego Serious Play method
        • Building evolutionary architectures
        • Code that fits in your head
        • Culture Agile
        • Culture is everything
        • Domain-Driven Design: The First 15 Years
        • Dynamic Reteaming - The Art and Wisdom of Changing Teams
        • How to avoid a Climate Disaster
        • La liberté du commandement
        • Réaliser ses rêves, ça s'apprend
        • Refactoring at Scale
        • Succeeding with OKRs in Agile
        • Team Topologies
        • The Good Life
        • Tu fais quoi dans la vie
        • Who Does What By How Much?
  • My Activity
    • Retour sur mon année 2020
Powered by GitBook
On this page
  • Start by establishing our plan
  • 1) Extract the amount calculation
  • 2) Extract the credits calculation
  • 3) Create a specific Printer implementation
  • 4) Putting whole together

Was this helpful?

  1. Software craftsmanship
  2. Katas
  3. Theatrical players refactoring Kata

Let's refactor (OOP style)

Theatrical players refactoring Kata in Object Oriented Programming

PreviousTheatrical players refactoring KataNextLet's refactor (FP style)

Last updated 4 years ago

Was this helpful?

Start by establishing our plan

We can use the to do so.

Here is mine :

1) Extract the amount calculation

Create the interface

AmountCalculator.java
public interface AmountCalculator {
    Integer compute(Integer audience);
}

Extract the 2 implementations

public class ComedyCalculator implements AmountCalculator {
    @Override
    public Integer compute(Integer audience) {
        return 30_000 + 300 * audience + (audience > 20 ? 10_000 + 500 * (audience - 20) : 0);
    }
}
public class TragedyCalculator implements AmountCalculator {
    @Override
    public Integer compute(Integer audience) {
        return 40_000 + (audience > 30 ? 1_000 * (audience - 30) : 0);
    }
}

Create a factory to retrieve the implementation based on the type

AmountCalculators.java
public class AmountCalculators {
    public static Integer forTypeAndAudience(String type, Integer audience) {
        Map<String, AmountCalculator> types = new HashMap<>();
        types.put("tragedy", new TragedyCalculator());
        types.put("comedy", new ComedyCalculator());

        if (!types.containsKey(type)) {
            throw new Error("Unknown type: " + type);
        } else {
            return types.get(type).compute(audience);
        }
    }
}

2) Extract the credits calculation

private int volumeCredits(Invoice invoice, Map<String, Play> plays) {
    int credits = 0;
    for (Performance perf: invoice.performances) {
        Play play = playForPerformance(perf, plays);
        credits += Math.max(perf.audience - 30, 0);

        if ("comedy".equals(play.type)) {
            credits += perf.audience / 5;
        }
    }

    return credits;
}

3) Create a specific Printer implementation

Create types (simple POJOs) that represents a Statement

@Data
public class Statement {
    String customer;
    List<Line> lines = new ArrayList<>();
    Integer totalAmount;
    Integer volumeCredits;

    void addLine(Line line) {
        lines.add(line);
    }
}
@Data
@AllArgsConstructor
public class Line {
    String name;
    Integer amount;
    Integer audience;
}

Create a Printer interface

Printer.java
public interface Printer {
    String print(Line line);
    String print(Statement statement);
}

Extract the current print logic in a TextPrinter class

TextPrinter.java
public class TextPrinter implements Printer {
    @Override
    public String print(Line line) {
        return String.format("  %s: %s (%s seats)%n",
                line.getName(), Formatter.usd(line.getAmount()), line.getAudience());
    }

    @Override
    public String print(Statement statement) {
        return String.format("Statement for %s%n", statement.getCustomer())
                + statement.getLines().stream().map(this::print).collect(Collectors.joining(""))
                + String.format("Amount owed is %s%n", Formatter.usd(statement.getTotalAmount()))
                + String.format("You earned %s credits", statement.getVolumeCredits());
    }
}

4) Putting whole together

StatementPrinter.java
public class StatementPrinter {

    private Play playForPerformance(Performance perf, Map<String, Play> plays) {
        return plays.get(perf.playID);
    }

    private int volumeCredits(Invoice invoice, Map<String, Play> plays) {
        int credits = 0;
        for (Performance perf: invoice.performances) {
            Play play = playForPerformance(perf, plays);
            credits += Math.max(perf.audience - 30, 0);

            if ("comedy".equals(play.type)) {
                credits += perf.audience / 5;
            }
        }

        return credits;
    }

    private int totalAmount(Invoice invoice, Map<String, Play> plays) {
        int amount = 0;
        for (Performance perf: invoice.performances) {
            Play play = playForPerformance(perf, plays);
            amount += AmountCalculators.forTypeAndAudience(play.type, perf.audience);
        }
        return amount;
    }

    public String print(Invoice invoice, 
                        Map<String, Play> plays,
                        Printer printer) {
        Statement statement = new Statement();
        statement.setCustomer(invoice.customer);

        for (Performance perf : invoice.performances) {
            Play play = playForPerformance(perf, plays);
            int amount = AmountCalculators.forTypeAndAudience(play.type, perf.audience);
            statement.addLine(new Line(play.name, amount, perf.audience));
        }

        statement.setTotalAmount(totalAmount(invoice, plays));
        statement.setVolumeCredits(volumeCredits(invoice, plays));

        return printer.print(statement);
    }
}

Our code is now ready to be extended with the new HTLM printer.

mikado method