Okay, it’s time for a survey article. This is the kind of article that I hinted at in If this were a book. The general plan of attack in articles like this will be:
Outline a single simple example of implicit coupling.
Explore solution options in a wide variety of different (types of) programming languages.
This could all go horribly wrong, so please bear with. I have no idea where this might take us. But I haven’t seen this done anywhere else before — not even Martin Fowler has written anything similar, so we’re on our own. I think it’s fairly inevitable that I’ll miss things, so please do chip in when you see something I haven’t thought to consider…
The example I want to use today is that of a function that takes two parameters of the same type or no type. The caller’s code is coupled to the parameter order, because passing them in the wrong order would mean that the function will probably do something unintended at runtime. The coupling is usually implicit, so how can we arrange the function’s signature so that it is obvious in the caller’s code how the values supplied map to the function’s parameters?
Note that even if the function checks the values it was passed and rejects them — for example by throwing an exception — when they are invalid, that’s a runtime check. It places nothing explicit in the caller’s code to clean up the coupling.
Solution options
Solutions to this problem naturally all involve changing the function signature. It seems to me that we have a few options, depending on what our programming language supports:
Change the types of the parameters to be different.
Use named parameters.
Don’t pass two parameters: group them into a structure.
Don’t pass two parameters: split the call into parts.
Let’s look at each of these options in turn…
Parameters of different types
This, of course, is only possible if our code is written in a language that has static typing. But it is also only possible if the parameters represent values taken from two different domain types. For example, in C# we might change
to
In this particular case this has the additional advantage that we’ve addressed some Primitive Obsession, but that won’t always be true.
But the most important aspect of this refactoring is whether we can now see the coupling explicitly in the caller’s code. And we can, because the caller must arrange to have instances of those new types available. So the caller will also change, to something like:
Thus at both ends of the coupling I can see the exactly what might break, without having to run a compiler or tests or the application itself.
However, if our language doesn’t allow for static types we could perhaps offer named parameters…
Named parameters
Some languages such as Ruby, C#, Clojure, Python etc allow the function to state that the caller must provide names when passing parameters.
So for example
This allows the caller to pass the parameters in any order, and again the coupling has been made locally explicit in the caller’s code.
However, if our language doesn’t allow for static types or named parameters we’ll need to change the number of parameters instead.
Over the years I’ve observed that code that is more habitable tends to have very few functions with more than one parameter. So I do generally feel that my preferred option, regardless of the programming language facilities I have available, is to group parameters into a structure or split the call into parts that have only one parameter each.
Pass a structure
If we can’t statically type our parameters, or give them static names — as, for example, in Javascript — we have no option but to avoid having multiple parameters at all. Most languages allow us to group things into larger structures, so we could do this:
As with all of the other options, the caller has to change such that it explicitly names the parameters, and so the Connascence of Position goes away.
I suspect many programmers might object to the additional syntax and dereferencing needed here, saying that the two-parameter version is simpler. But for me, that’s not what “simple” means. In the 4 Rules of Simple Design, brevity is our lowest priority. Here I’m adding syntax in order to make some coupling explicit. So this code tells the story better, and also is kinder to the caller.
Split the call
The major alternative to passing a structure is to split the function into two functions, usually arranged such that the second function is called on something that has the first parameter as internal state. Think of the Builder pattern, which is common in the Java and C# worlds.
In the Customer example I’ve been using in this article I don’t need anything as sophisticated as a Builder, because I can easily (I assume) split this function into two like this:
(Example in Ruby, but would work in any other language similarly.)
But what if there is more of a semantic connection between the two values? We’ll usually need objects or Builders or monads in order to accomplish this. For example suppose I have this Javascript code:
I could consider refactoring like this:
Aficionados of code smells may look at this and cry “Method Chain!”, but note that the methods are received by two different objects of two different classes. In fact there’s a stronger case here for complaining about violation of the “law” of Demeter, because in the refactored code the caller is now coupled to the existence of shelves, and thereby possibly to implementation details of the Warehouse. Yes, I think that’s true in this case — so I would probably have been better taking a different route and introducing an object:
In most cases, where I can find reasonable domain terminology for the intermediate type(s) this approach can significantly improve the code’s ability to tell the customer’s story.
So that’s a very quick tour of some refactoring options for dealing with this one kind of Connascence of Position. I’ve probably missed a lot of possibilities, so please drop them in the comments where you see things I haven’t.
Things to try
Review your code for functions with more than one parameter and try each of the options above as an alternative design. What forces are at work to encourage you to pick one option over another? Is the cost of the refactoring worth the improvement in habitability?
If you try this, please let us know in the comments. And please also point out those inevitable cases that I’ll likely have missed!