The Diamond Kata

So I've just run into the Diamond Kata (recursive definitions are FTW) and thought it'd be fun to try, so without having looked at the takes of others (and there are many, see below for a sample), I jumped right into it. In the Diamond Kata we have to draw a diamond shape of letters, like this one here:

--A--
-B-B-
C---C
-B-B-
--A--

So what does the requirement tell us? How would we describe the task in a sentence, that we could even communicate to our customers? If we focus on the top-right quadrant, and observe the symmetries, we can see that the diamond is made of a…

  1. sequence of letters from 'A' up to a given target
  2. arranged diagonally for the top right quadrant, then
  3. mirrored horizontally for the top half, and finally
  4. mirrored vertically to build the entire diamond

All four stages appear, and will indeed turn out to be simple functions, so we can already see the diamond function as a straightforward composition of these four sub-functions, or workers. And so I created the four workers with a simple testcase each, that I later discarded, as I don't test private implementation details, then created the diamond function as a compose operation and added some tests to verify the result. And so I saw no room/need/use for TDD at all, as after a minute or so thinking, there was nothing left to discover, no hidden structures to emerge, no design to drive.

Here's the resulting production code. I'll omit the tests, as they're trivial.

def diamond(max)
  diamond_mirror_v(diamond_mirror_h(diamond_diagonal(diamond_sequence(max))))
end

private

def diamond_sequence(max)
  'A'.upto(max).to_a.join
end

def diamond_diagonal(str)
  dashes = '-' * (str.length - 1)
  0.upto(str.length - 1).map { |i| dashes.dup.insert(i, str[i]) }.join("\n")
end

def diamond_mirror_h(text)
  text.each_line.map { |str| str.chomp!; str.reverse.chop + str }.join("\n")
end

def diamond_mirror_v(text)
  text + "\n" + text.each_line.map { |str| str.chomp }.reverse[1..-1].join("\n")
end

Take a look at the line in green, imagine it in a better readable function composition format, and consider just how expressive it is. That sole line holds the entire logic, while the workers are all simple, independent, utility functions. Also note, how this decomposition was not achieved via TDD, but by clearly formulating and breaking down the problem statement. What's more, this approach could even work in more complex situations, as the workers don't need to be this trivial, only a strong separation of concerns is necessary (kind of SRP style).

If I look at the production code that resulted from the various TDD approaches – incremental or iterative –, by Alistair Cockburn, Nat Pryce, Emily Bache, Ron Jeffries first or second attempt, Sandro Mancuso, George Dinwiddie or others, nowhere do I see the four structural forces of the problem being this clearly separated and this transparently composed. It appears to me, that despite TDD promising focus on design, the solutions turned out to be more or less technically structured, with domain concerns taking second seat only.

While other approaches are also possible, a solution that claims to be good design should be similar to the one presented here, in that it must break the overall complexity down into individual, orthogonal, borderline trivial aspects. What's of most concern to me, that none of the TDD based end products (that I looked into) ended up with such a clear decomposition, e.g. many are fighting with the complexities of letter and placeholder sequences that are only induced by not separating what I referred to as sequence, diagonal and mirroring.

And so I wonder.

How much should we be focusing on following the rules of TDD and how much on understanding the problem? Is it possible to fully address the domain design while following the TDD process? Why did none of the TDD based solutions separate the structural forces of the kata? Who is to say, the process of TDD will not obscure our sights of the problem domain in more complex situations, just as it seemed to do here? If there's a risk of producing inferiour code with TDD, then why should we do it? And how representative is this kata?

Don't worry, this is just me thinking out loud.

Flamebait over.

stray_words/diamond_kata.txt · Last modified: 2017.01.17 17:21 by infinitary