String Calculator kata as a collaboration of entities
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 structural decomposition.
Before we dive into this solution, we need to look at some key characteristics. This solution use some collaborative objects. It doesn't have any state. The decomposition happens in multiple files where the collaborators are responsible for handling their own input format.
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 does not reflect any behavior, there is some boilerplate code, and some methods mix up the responsibilities.
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 an input as a piece of data with its own behavior. To make this decomposition more understandable (and because the entities have shared behavior), we start the decomposition by introducing a SmartString abstraction. Each derivative of this abstraction is responsible for handling its own input string format (an empty string, a string without delimiter, a string with a custom delimiter). The add()
method is the entry point that contains only an explanatory variable with an instance and an operation on this variable.
I like this implementation. This is a real object-oriented decomposition. But the whole implementation has a big disadvantage - the requirements are not reflected in the entry point, neither the SmartString behavior. Btw, the sum()
method mixes up the responsibilities on purpose, because this is the place where you can see the sequence of steps of the solution. Otherwise, the steps will be spread around different methods which will make it much harder to understand.
It follows the general programming principles. But it is definitely not very DRY because of the shared behavior. It complies with SOLID principles, but only to some extent. It violates the SRP in the sum() method. It partially violates the OCP principle. When a new requirement related to the input string format comes into play, it is easy to modify and integrate this new behavior. However, if it is necessary to make changes to the existing behavior, it is going to be hard. If these changes require different behavior for different entities, it is going to be even harder. But, it should make it.
If I need to improve it somehow, I would probably make it even more similar to pattern matching pattern from functional programming. The logic of the from()
method resembles something similar to this pattern.
Solutions in the series:
- a single unit of work with state
- a single unit of work without state
- a bundle of entities
- a bundle of behavior
Comment this page: