#begin
In todays blog we will be diving into chapter 9 of A Philosophy of Software Design. In this blog we will discuss one of the most fundamental questions of software: Better together or better apart?
Better Together Or Better Apart?
What ever that means. John starts this chapter with the fact that one of the most fundamental questions in software design is to determine whether, given two pieces of functionality, should they be implemented together in the same place or should their implementations be separated. This indeed is a though question in many situations, but Uncle Bobs, SRP can help you discover the answer to it. This is not to say that the SRP will always be the way to solve the issue, and applying it is always the best way to go. But the question about coupling things or keeping them together returns on all levels of abstraction in game development like, methods, classes, services or entire systems.
The main goal for deciding when things belong together or should be separated is to reduce the overall complexity of the system. In many cases it might seem better to separate larger things into smaller components and then build you system that way. To me, this makes a lot of sense.
This also one of the key takeaways from another great book I read called “The structure of computer programs”, which I’m not getting into at this moment becasue it is far beyond the scope of this blog.
But then again, subdividing things into smaller components can is some cases also increase the complexity and cognitive load for a developer. I’ll give you simple example; Have you ever ran into the situation where you are trying to find out how things are setup in a given project, only to find out that everything is implemented with interfaces, and thus you do not know the concrete objects that are being used. And, then find out, that the objects that implement these interfaces, have no business doing so and seem to have just been implemented this way for convenience, but not based on the domain. This creates so much confusion for a developer because domain concepts are implemented on seemingly random object, just because it was an easy way to reference each other.
John identifies a couple of side-effects of subdivision that will can increase complexity. First of all, some complexities come just from the number of components. The more components, the harder it is to keep track of them. Second, subdivision can result in additional code to manage components. So this means the cognitive load of the developer increases since you need to manage these objects interacting, often through abstract interfaces though. Third, subdivision creates separation. Things that used to be together are now apart, like for example functions that were together, which get extracted into new objects. Fourth, subdivision can lead to duplication.
When I read all this, I still firmly believe that this originates from naive or dogmatic implementation of the SRP. When you create a new class or interface for everything you can come up with, subdivision will indeed lead to these side-effects. But if you give it some thought, and think about the number of stakeholders a class has, you probably wont run into this as much as John thinks you will. I also think experience plays a large role here. Experience plays a large role in how to properly, strategically in this book’s terms, separate responsibilities and not naively separating everything you can.
Don’t get me wrong, this is great advise and applicable in many situations, but just as with many things we discussed from the Clean Code book, there’s nuances. Don’t apply software design practices in a dogmatic way because you will quickly find you make things complex or over-engineer things.
But let’s continue with the book. John goes on with some advise how to indicate code is related. First, they share information, this is kind of obvious right. Still this can also be interpreted wrongly. I mean, if your Enemy class shares information with your weapon class, that does not mean these classes should be merged into a single class. Second, Classes are used together. If you use one class, it’s highly likely you will use another class as well. Third, classes overlap conceptually, in that there is a simple higher-level category that includes both of the pieces of code. And Fourth, It’s hard to understand one of the pieces of code, without looking at the other.
John presents some examples of when to subdivide and separate code and when it might be better to bring things together. But there’s also some advise and he raises some red flags as well. Let’s quickly discuss these things.
He says to bring thing together if information is shared. He gives an example where functions share and operate on the same data or information. In this case it’s a good idea to bring these functions together. And I’m not sure I agree. It seems perfectly logical to do this, but if we look at Clean Code, these functions might have been created in order to raise the level of abstraction and documentation purposes. Often these functions are private and rather shallow, but they’re there to increase readability and leveling the abstraction level. But on the other hand, John is right since creating these dependencies between functions seems useless and increases complexity. I think this has to do with the nature of these functions. So if you have information sharing between functions, which are both public, and reside in different classes, you might be better off following John’s advise on this one.
Next he talks about bringing things together to simplify the interface. He says it very nicely so I’ll quote: “When two or more modules are combined into a single module, it may be possible to define an interface for the new module that is simpler or easier to use than the original interfaces. This often happens when the original modules each implement part of the solution to a problem.”
And I agree with him, embedding two modules, behind a single interface. May that be two classes, behind an interface definition, or even two entire systems behind some web API. Will simplify the interface if these modules are not supposed to be used separately. Then you can make these classes internal to some namespace and not worry about it.
Next-up he talks about bringing things together to eliminate duplication. So if you see some code repeated over and over again. You should see if you can reorganize it to remove that duplication. John says that in many cases you can factor out the repeated code into a method, and simply call that method. I think Uncle Bob would agree with this since he basically says the same thing. Just remember the discussion we have in the previous blog about, not all seemingly duplicated code, being actual duplicates.
Next he talks about separating general-purpose and special-purpose code. This seems like a no-brainer as well but it can be really difficult. Sometimes you’ll have written some really general-purpose code but there are special branches in the code that are there for some special-purpose requirements. So in this case you should find another solution to run this special purpose scenario and separate the special from the general purpose since it also often leads to code being repeated and duplicated which is a red flag. This will reduce the complexity of your general purpose module. Having special-general purpose mixture modules is a red flag as well.
He then has a very nice quote, in printed in bold, and it says; “Each method should do one thing and to it completely”. This is a very bold statement… see what I did there? He means that users should be able to use a function, or interface without having a high cognitive load and the need for combining method calls in order to get something done.
He then raises another red flag and it’s about this notion of conjoining methods, it says: “It should be possible to understand each method independently. If you can’t understand the implementation of one method without also understanding the implementation of another, that’s a red flag. This red flag can occur in other contexts as well: if two pieces of code are physically separated, but each can
only be understood by looking at the other, that is a red flag.”
To me, this again sounds like someone naively implemented the SRP and went totally overboard.
Concluding arguments for this chapter are as follows and I’ll simply quote Professor Ousterhout: “The decision to split or join modules should be based on complexity. Pick the structure that results in the best information hiding, the fewest dependencies, and the deepest interfaces.”
And that’s a wrap for chapter 9, Better together or Better apart. There are a number of contradicting opinions compared to clean code in here which I tried to explain. I think you need to find a nice middle ground since both John and Uncle Bob have valid points. Don’t do things religiously but take multiple things into consideration and take the best of both worlds.
#end
01010010 01110101 01100010 01100101 01101110.
Recent Comments