String Calculator kata as a collaboration of behavior

These are some notes on a String Calculator kata implemented in object-oriented way as a collaboration of entities. By entities we will consider different implementations of the same abstraction that emerged from the different input string formats. This kata implementation is object-oriented and tends to follow the object-oriented behavioral decomposition.

Before we dive into this solution, we need to look at some key characteristics. This solution use some collaborative objects. It uses state to keep the references to collaborators. The decomposition happens in multiple files where the collaborators are responsible for a specific behavior.

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, there is some boilerplate code, and each entity contains only its own responsibility.

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 big idea behind this decomposition is to treat behavior as separate entities and compose them together to perform a task (it reminds me the functional composition somehow). We start the decomposition by introducing different abstractions for different behavior (Extractor, Validator, Limiter). Then, we either inject the collaborators or we create their default implementations. The add() method is the entry point that keeps all the high-level policies represented by the calls to the collaborators. Looking at the code, we can quickly identify the responsibility of each collaborator.

I like this implementation. This is is a real object-oriented decomposition. It might seems a little bit wordy (uncolored places in the code map). On the other hand, it gives true flexibility and is very testable. What is even more important that it does not mix up the responsibilities. Each collaborator does exactly what it is expected to do. The code follows general programming principles and is DRY. It complies with SOLID principles. When a new requirement related to the input string format comes into play, it is easy to modify the existing code or provide a new collaborator. If it is necessary to make changes to the existing behavior, they are easy to implement too. It is even possible to extend the behavior without touching the production code.

For now, I wouldn't change anything in this implementation. However, if the number of input string formats increases, it is going to become too complex and should be restructured.

Solutions in the series:

Comment this page: