I thought a little bit whether I should somehow reply to Ilker's posting - and then I started to get the feeling that that problem (my problem?) was not a good kata, after all. And alone this feeling makes it worthwhile to write about it, I thought.
How would you go about solving this puzzle? Well, I think a standard idea is to write an acceptance test first. Why? Certainly, we would not want to make this test run successfully - it is much too complex for an initial unit test in a TDD culture. No, the reason is that in that step we set the stage for interesting properties of the solution - in this case, the API. Here is my acceptance test:
[Test]
public void AcceptanceTestUsingIlkersData() {
IEnumerable
"A1", "B1", "A2", "A3", "C1", "D1", "E1", "E2" };
IEnumerable
"F1", "D2", "B2", "B3", "A4" };
IEnumerable
IEnumerable
IEnumerable
Pick.Akin(ceoList, ctrlList, (a, b) => a[0] == b[0],
out ceoListAfter, out ctrlListAfter, out akinList);
CollectionAssert.AreEquivalent(ceoListAfter, new[] {
"C1", "E1", "E2" });
CollectionAssert.AreEquivalent(ctrlListAfter, new[] {
"F1" });
CollectionAssert.AreEquivalent(akinList, new[] {
"A1", "B2", "A2", "A3", "B3", "A4", "D1", "D2", "B1" });
}
... aaah, don't hit me - or at least not that hard! Already at this point, I have made quite a lot of decisions that could have been decided differently:
- Implementing it in C# (instead of F#; or SQL)
- The API function is called Akin, on a class Pick.
- The API function is a static function.
- The API function is a generic function - although you do not see this.
- The input are two sets as well as a "being similiar" function.
- The result is returned in three out parameters.
- The parameters are actually of type IEnumerable
, with an additional comment that the order is not important.
- Know the problems of what you are going to introduce.
- A set-based language (like SQL ...) would a better fit - after all, we had to annotate all parameters with "order is not important", which is a direct contradiction to the basic definition of IEnumerable.
- The name PickAkin is a classic function name: "do what" - similar to FindMatching, InitLazily etc. Splitting the externally defined name into class and method is strange.
- Static functions have problems with testability, with injection, with state (implicit singletons).
- Generic functions add additional comprehension complexity; and need additional tests if the type parameter can also assume struct types.
- Passing functions as delegates creates problems like "access to modified closure"; a classical abstract function in an abstract class with an override in the concrete application does not have this problem.
- Out parameters need to be defined beforehand in the client code. At the minimum, this requires additional names for the result sets. Instead, the result could be returned in a classical way as the return type - probably in a special result type. Or, it could be returned into ICollection
parameters by using Add on them. - Using immutable set types would directly capture the intent of the parameters.
Slow going, isn't it, this kata?
If you do this in a dojo session with 2, 3, 4 people, you might have completely deadlocked at this point - because all these assumptions and decisions have to be brought out. Of course, if you are versed dojo-ers, you probably know how to resolve such discussions. Still, I think that trying out e.g. three different designs alone in this simple case will teach you all (the 2, 3, 4 of you) quite a few different viewpoints. Isn't that the idea of a kata, after all? Doing it over and over to find many "right ways"?
Next part: The PickAkin Kata - part two
No comments:
Post a Comment