the problem: the diamond kata
I use Seb Rose description as it is simple and efficient.
Given a letter, print a diamond starting with ‘A’ with the supplied letter at the widest point.
For example: print-diamond ‘C’ prints
A
B B
C C
B B
A
I heard a lot about this kata. Recently1, it was the main subject of a two hours workshop at the awesome NCrafts conference. So long time after the initial battle2, I decided to give it a try.
my solution
the idea
Well, as usual, when I’m told that something is difficult or impossible, I just don’t care and do it as I will have if no one told me anything. So I apply TDD as I usually do: red, green, refactor. And that’s all.
Write a test, make it pass, clean the code. One step at a time.
the first test
So let’s start with the first test.
Test is Red, it’s time to make it go to green:
Yep, that’s a stupid implementation. But it works :)
And quite clean by itself. So let’s add another test.
a second test
And make it pass stupidly.
And it’s green, so it’s time to refactor. But I can’t see duplication yet.
Well, there are twice the same “ A “ line in the ‘B’ diamond. But I can’t give a meaning to it. I probably don’t have enough data.
So, I’ll keep things like that for now, and add a third test.
and the third
As I try to be coherent, my way to make it green won’t differ from the previous one :)
Yep, again that is a stupid implementation. Because at this moment I don’t care being clever, doing nice, clean things. At this moment I just want to be sure that the test I wrote is the one I intended to write.
By making it goes green with a simple solution, there are very few risks I introduced a mistake in the implementation. So when I see the test going from red to green, I can be confident on my test.
That’s my way to test my tests.
So now, I may have enough materials to see if I can make some interesting things appear.
time to start refactoring
There should be some logic in the letters and spaces distribution. As I don’t understand it yet, I will artificially separate them. Yes, I’m going to create kind of artificial duplication.
Now I can notice that first and last lines are z spaces - A - z spaces
.
If a diamond ‘A’ has a size of 1, ‘B’ of 2, ‘C’ 3 and so on, then z = (size - 1)
Great, lets put that logic in a method.
I also notice that other lines follow the same pattern : x spaces - a char - y spaces - same char - x spaces
So lets make that appear in the code.
There is definitely something that tie everything, but i don’t see it yet.
a fourth test
Let’s add the ‘D’ diamond. I won’t show you the test, it the same than previous but with a ‘D’ diamond. Make it pass by adding this in our current solution.
From now, I will only show you the refactoring on the ‘D’ diamond.
All those integers must have some kind of relationship. Maybe we can figure it by rearranging them.
Let’s introduce the width
of the diamond.
I draw some diamond on a paper and manage to figure quite rapidly that the width
is size * 2 - 1
Well, I think I got it. Let make appear a floor level.
Yes, every lines really look like the same now, we can give it a name.
That’s better, but we are not done yet.
There is a pattern in these calls. Let’s see what happend if we renumber floors so they become a sequence, from +size
to -size
Well, there seems to have a loop hidden there. The problem to introduce it are those characters in the parameters of diamondWall
call.
But we can easily get rid of them as the character used in a floor can be compute from the size of the diamond and the current floor.
and finally
thoughts
My first attempt on that kata took me around 2hours in a train3.
As I usually do I’ve made things with very small steps in order to keep my test green all the time. I tried to let the algorithm emerge from the refactoring, and not the opposite.
Of course, it did not emerge by itself. With that kind of problem, I had in mind that there will be some kind of loop in the solution. So my refactorings aimed to make that loop appears: introducing deliberate duplication so that each line looks the same, then unifying indices.
Introducing one change at a time allow my test to be very efficient in helping me to find the errors I’ve made4.
The full code is available on my github. Look in the branches :)5
next
Once there, if it was real code, I will probably give name to size -2
and -(size - 2)
. Maybe attic and cellar to stick with the building metaphore. And size
should probably become height
.
I also don’t really like the use of a Character
everywhere will in fact it is a kind of diamond. So i’ll introduce a DiamondKind
class to hold the Character
and size
6, width
calculation. Maybe other Character
operations too7.
Maybe a Floor
class too8.
The “A” diamond would probably become a constant with a name.
image : By kubotake CC BY 2.0, via Wikimedia Commons. During a solar eclipse, in french, the third contact is also called “Effet diamant” or diamond effect.
-
well, it was recent when I started write that post. ↩
-
started I think with this post Recycling test in TDD by Seb Rose. ↩
-
and I made a lot of them. +1/-1 shifts are my personal hell :) ↩
-
in the branch 2015.05.23-step_by_step, I made a commit at each step with some explanation of the purpose of that step. Mostly, it is what you find in this post. ↩
-
or height ↩
-
like a
charForFloor
↩ -
but currently the use of
floor
is an implementation detail, not sure if it’s a good idea to give its own class that will become public. ↩
Poursuivre la discussion
Envie de réagir à l'article ? Il suffit de me laisser un message via la page de contact, sur mastodon @avernois@piaille.fr ou un billet chez vous.
Vous pouvez aussi proposer des modifications via le source de cet article sur gitlab.