So much of good software design comes down to separating the WHAT from the HOW.
WHAT we want should be expressed in the interfaces of the services that we create. It should clearly state WHAT the service provides, the parameters that it needs to take in, and the value that it returns. Our services should provide strong contracts that guarantee a range of specified behaviors.
Think about the result that the client wants. Think about being your own customer. This is how we want to think when we design APIs. We want to take our callers perspective because this is how we designed well-encapsulated systems that are straightforward to interact with.
One of my working definitions of the key code quality called encapsulation is about hiding HOW we do something by only saying WHAT we do. This is a form of hiding implementation details and we want to do this in the software that we create because when we do, it lessens the impact of change when it happens in our code.
Most software is written in such a way today that it is intertwined with itself and this makes it very difficult to extend software and add new features. According to several studies that I reference in my book, Beyond Legacy Code, it cost twice as much to make minor enhancements to existing software than it took to create that software in the first place.
When I see statistics like that I realize that we have to build software differently as an industry. It’s not like we don’t know what to do. We’re not like doctors were in the Middle Ages, without germ theory or a good understanding of biology to draw upon.
We have the practices of Extreme Programming and good design principles and practices that are known throughout our industry. But the knowledge of good programming practices throughout our industry is quite uneven. One of the main reasons for this is that our industry is growing so rapidly. Software developers are some of the most in-demand professionals today and the supply of available developers can’t keep up.
Uncle Bob Martin says that the base of professional software developers doubles every five years and has done so for decades. If he’s right, then it means that more than half of the people on every team have less than five years of experience. Couple this with the barrage of frameworks and languages and tools and methodologies that people are confronted with daily and we begin to see why there is a big gap in understanding of exactly what software development is or should be as a profession.
But regardless of the standards and practices that we use to actually build our implementations, one of the most valuable practices that I have found as a developer is to find ways of hiding the details of how I implement a feature from my callers. That way my caller won’t depend on those details so I’m free in the future to change them without affecting my callers.
That’s the real payoff of being implementation independent. When we can hide implementation details it means that we can change them later without affecting other code. However, encapsulating implementation details is a subtle business. We have to be careful about the assumptions that we make.
I remember I was working with a team of developers and teaching them one of my very favorite techniques for giving code greater extensibility by providing a class with a static method to new itself up. I called this method getInstance(). A lead developer saw calls to getInstance() in my code and asked me why I was using so many Singletons.
“Why do you think I am using Singletons?” I asked.
He pointed to the getInstance() method calls in my code and said that they were creating Singletons. But my getInstance() method wasn’t doing that, it was just newing up an instance of itself.
Why did he think that it was creating a Singleton? Because the Gang of Four used that method name in their book, Design Patterns: Elements of Reusable Object-Oriented Software, when
From the client’s perspective it simply wants to get an instance of an object and it should not have to care whether it’s being given a Singleton or not. What happens when we realize that just one single instance isn’t enough to service all of our callers, so we refactor to a Doubleton or Tripleton or an object pool. When I do this do I tell my clients they have to change the name of the getInstance() method because it is no longer invoking a Singleton?
Clearly, this is a slippery slope. It’s not the clients concern how I manage the instance internally. The client simply wants to get an instance of an object and leaves it up to the object to decide how it should fulfill that requirement in the best way possible.
This is what we mean by a separation of concerns. Client’s don’t need to be concerned with the details of their caller’s work. I also refer to this as assertive code where objects are responsible for themselves and the state they manage. Giving objects the ability to be responsible for themselves, including instantiation, lets them be more autonomous so we build software that’s more functionally independent and straightforward to reason about.
Note: This blog post is based on a section in my book, Beyond Legacy Code: Nine Practices to Extend the Life (and Value) of Your Software called Seven Strategies for Product Owners.
Previous Post: « Help Developers Understand Why and for Whom
Next Post: Answer Questions Quickly »