Bend, or Break
In this chapter we will cover lots of interesting topics that have stood the test of time. Many of these topics will already be known to you as they are ingrained in our industry over the past 30 years. Remember, this book was written in 1999! First we will talk about the Law of Demeter, sounds familiar? Uncle Bob talks about this law in Clean Code as well. Do you still remember what it means? I won’t spoil it just yet. Next we will dive into meta-programming which in this case leans towards configuration. But here also, remember Prof. Ousterhout’s advice to not make everything configurable because you are just relocating complexity in your code and making it somebody else’s problem. Take on some suffering of your own, pull complexity downwards and fix it yourself!
Then we will talk about temporal coupling. This term probably rings a bell too, yet I don’t think we have talked about it before on this blog. But no worries, we will today:) And, this chapter is followed with a chapter called It’s just a View which provides some solutions to loosen up coupling. We also talk about BlackBoard architecture. This is an architecture pattern you have probably heard of. I think I’ve only implemented it once, for a school assignment. So let’s see if we can refresh some knowledge.
And that’s all the topics we will talk about in this episode. So I would say the first half is about explaining coupling and the latter half is about some strategies to break it.
So let’s go and talk about the Law of Demeter once again and see if we can learn something new about it.
Decoupling and the Law of Demeter
So if I recall correctly the Law of Demeter means something along the lines of: talk only to your direct dependencies, not to strangers. So to give a simple example don’t call A.B.C.MyFunction() but encapsulate that function call on C somehow in B. A doesn’t have a direct dependency on C and thus C should not be called by A.
By keeping such calls at bay, you can drastically reduce coupling in your code. Andrew and David remind us of previous topics; orthogonality and designing by contract. These concepts promote what they call ‘shy’ code. Shy code is beneficial for two reasons it doesn’t reveal itself to others, and doesn’t interact with many ‘people’. So this is pretty much in line with the Law of Demeter. We must organize our code in modules, packages, components, gems or what-ever your favorite language nomenclature for packaging code is. In the case of Unity3D we could use the term Package, or maybe go a bit more in detail and go to the level of DLL’s or even individual namespaces. We need to limit interaction between packages and most certainly break cyclic dependencies between them as they are most problematic.
Minimize Coupling
So why all this talk about minimizing coupling. This has dominated our industry for such a long time. It has grown into an even larger topic since they entire micro-service boom since these services must inherently be decoupled or else you end up with a distributed monolith, which is one of the anti-patterns of modern micro service architecture.
But, what’s wrong with coupling? In some way there will coupling must always exist, so in principle there isn’t much wrong with it.
Andrew and David put it this way and I quote: “We don’t need to be as paranoid as spies or dissident. However, you do need to be careful about how many other modules you interact with and, more importantly, how you cam to interact with them”. So, what they are saying is that coupling isn’t inherently bad but you must know what your code is coupled with and more importantly why this coupling exists because, if you don’t even know why some code is coupled there is no way for you to manage that. This will most certainly lead to problems down the road while building or deploying for example.
Then they give a really nice analogy about the Law of Demeter, or coupling in general. They talk about a scenario where you are remodeling your house, or maybe building a new house from scratch. You employ a contractor to fix everything and he in turn hires a bunch of subcontractors to do specific tasks. You do not interact with these subcontractors because that’s what you hired the general contractor for. He takes on all the headaches for dealing with them. You should be unaware that any of these subcontractors had anything to do with the work that was done.
This is exactly what the Law of Demeter says. Talk to friends, not to strangers. When we interact with some piece of code, we want to get some domain object as a return value. We do not want some random third-party object as a return value because then we are coupled to that third-party object. Proper use of interfaces or an adapter pattern is required here. When you expose too much in public API’s, may that be as input to the API or output you increase the risk of unrelated change somewhere else in the system that will affect your code. So a change in totally unrelated code, will trigger compile errors in your code. Yeah, this is bad and we have most likely all experienced this.
So for your public API’s try to only communicate through primitives and domain objects. Don’t throw let’s say StringBuilders around. A StringBuilder is an implementation detail, not a value to be passed into a public api.
So these relationships between objects can quickly explode into a giant web of (unwanted) dependencies. David and Thomas mention 3 ways you can observe these symptoms:
1: In large C or C++ projects, the linking stage of the build process might take very long.
2: ‘Simple’ changes in one module can propagate to others
3: Developers are afraid to change the code because they don’t know what else is impacted.
Do these sound familiar? Let’s be honest, yeah they probably do. Systems with many unnecessary dependencies are very difficult to reason about and thus very hard to maintain and change. They are also brittle and highly unstable. So by adhering to the Law of Demeter we can keep dependencies at bay.
The Law of Demeter for Functions
Up till now we have only spoken about the Law of Demeter in the context of source code couplings. But in The Pragmatic programmer Andrew and David go into this topic a little deeper and talk about the Law of Demeter for functions. I totally forgot about this section so let’s see what it has to say.
So to quote the authors on what the Law of Demeter for functions actually is, here we go: “The Law of Demeter for functions attempts to minimize coupling between modules in any given program. It tries to prevent you from reaching into an object to gain access to a third object’s methods.” Hmm so this is actually the example I gave before to not call a function on object C from an object called A through B. And this makes total sense. The Law of Demeter tells us to no use a reference of Object C somewhere in A, and the Law of Demeter for functions tells us to not call functions on C. So, in some sense I think the Law of Demeter for functions is a more complete definition because it includes both references and functions. As calling a function on C requires a reference anyway.
Does it really make a difference?
But does this actually matter? In this next section Andrew and David include some research done on this law. They mention that classes with higher response sets are more prone to error than classes with smaller response sets, and a response set is defined by the number of functions directly invoked by methods of the class. By following the Law of Demeter you can limit the response set of a given class and thus in theory reduce the number of errors. So classes following this law tend to have lower error counts.
David and Thomas claim that using the Law of Demeter makes your code more robust and adaptable; but it comes with a cost. When applied naively, developers tend to write lots and lots of wrapper functions to expose lower level methods. These methods simply forward function calls to their dependencies.
I think this is bad design because this exposes implementation detail just as much as doing it without the function wrappers. You might break direct coupling but the coupling is still there. So to fix this we must remember one of the early episodes about clean code. As mentioned before, Uncle Bob talks about the Law of Demeter a lot as well. He says exposing these wrapper functions is not the only way to apply the law.
According to Uncle Bob, a cleaner way of doing it, is to change your style of coding. So instead of asking for stuff through getters or example, change into a more command driven API. I think in the specific example he gives in Clean Code he refactors some code that deals with a file write. So instead of class A.GetFileWriter(path) => filewriter, you could change that API into A.WriteFile(path, content) => void.
And I agree with this strategy a lot. I apply it where ever I can since it really does result in more robust code in my experience.
Another thing to keep an eye on while applying the Law of Demeter is performance. Although this might be less of a problem now than it was 30 years ago. All these function calls produce run-time overhead. Until now, I’ve never encountered a situation where the sheer number of function calls is actually a performance drag. There are often more powerful dragons to slay when it comes to performance; especially in a Unity3D setting. Like crappy inefficient update loops, shaders and misaligned garbage collection, or extensive logging on the main thread.
And that’s all they have to say about the Law of Demeter, for now.
#end
01010010 01110101 01100010 01100101 01101110
Recent Comments