String Calculator kata as a single unit of work without state

These are some notes on a String Calculator kata implemented in an “object-oriented” way as a single unit of work without state. Why the air-quotes are used? Because it is not true object-oriented ways of doing things, even though the class keyword is used. This kata implementation tends to be sort of procedural, but in a good way.

Before we dive into this solution, we need to look at some key characteristics. This solution does not have/use any collaborative objects. It doesn't have any state. The decomposition happens only in one file inside one single class.

Code map and responsibilities

More about responsibilities was written in the introductory article. Here is the summary:

  • extraction of numbers (we extract numbers from different string formats) – a high possibility of change
  • validation of numbers (we validate the extracted numbers) – an average possibility of change
  • filtering of numbers (we filter the numbers before the final operation) – an average possibility of change
  • addition of numbers (at we end we add up all the numbers) – a low possibility of change

Let's take the the solution's code and color the responsibilities on its code map. In the picture, we can definitely see that the entry point reflects all the behavior and each method contains only one responsibility that it's handling.

Implementation code map

Originally, the kata consisted of 9 steps. I added the 10th step, which consists of questions that may help reflect on the final code. Here, I want to share an overview of my answers.

Reflections

The idea behind this decomposition is to reflect the responsibilities in methods as much as possible. The implementation has all the behavior inside a class without any dependent collaborators. The add() method is the entry point that keeps all the high-level policies. The intention to keep the responsibilities in the correspondent methods affects the code structure. Looking at the code, we can definitely see the responsibility of each unit of work.

I like this implementation. It is true that it is not object-oriented. But it is good enough, and I would prefer it over the implementation with state. In the entry point we can see all the behavior and the sequence of steps. So, this implementation is very simple to reason about and understand at a glance.

It follows general programming principles and it is DRY. It does not comply with SOLID principles, but only because it does not have any collaborators. Whether a new requirement comes into play or changes are required, the production code will be touched and rewritten in different places.

If I needed to improve this implementation somehow, I would move the check for the empty input string into the parseNumbers method. This low-level check inside the entry point violates the level of abstraction.

Solutions in the series:

Comment this page: