Wednesday, September 17, 2014

It’s the separation of concerns (SoC) that matters

If you take nearly any fundamental object-oriented concept and study its rationale you can tie it back to the benefits of separating concerns. The notion is simple really. It is easier to think about things, work with things, understand things and communicate things when we only think, work, understand, and communicate with the least amount of concerns as possible. And the goal of an object-oriented approach is to make complex things easier to think, work, understand and communicate about. 

We do it all the time in real life. If I spill a drink and the liquid is about to hit my laptop, I might quickly ask my wife for “something to wipe” this up. I don’t care if she gives me a paper towel, a dish cloth, a hand towel, or the shirt off her back. I am not concerned with details of the item she gives me. I only care that it can wipe something up. Likewise, I don’t quickly ask my wife for a “piece of absorbent cloth or paper used for wiping or drying and probably made in Mexico” either. She is not concerned with those details, she only needs to know I need “something to wipe” with.

Let’s take the concept of abstraction. In a nutshell it is the level at which we identify the details of something. A high level of abstraction means that when we are thinking, working, understanding and communicating about 5 things we do not have to be burdened by the details of those 5 things. A low level of abstraction would mean we are knee-deep in the details.  An object-oriented language has built-in features to help us write code that can work at the highest level of abstraction possible so we only work with the least amount of details possible. In other words, we only have to be concerned about very few details at a time.

By working to separate our concerns we end up with good abstractions. In object-oriented land this comes to play in abstract classes that are designed to define very general details of more specialized classes. For example a Towel is more abstract than a Paper Towel or Cloth Towel. When I wanted to wipe something up earlier a Towel is all I needed, I didn’t care if it was paper or cloth. In that case I could work at a higher level of abstraction, the details of the towel were not important in that scenario. If I write a routine to wipe up milk, that routine would take in a Towel as a parameter. You could call my routine regardless if you had a paper towel or cloth towel. If you one day get yourself a hemp towel, you could pass that in too. If I write a routine to wash a towel the routine might take in a Cloth Towel parameter because now I am concerned about a few more details, namely that the towel is made out of cloth.

Interfaces are yet another device that helps us separate concerns and allow us to be troubled with the least amount of details as possible.  If all I am concerned with is that a thing can be used to wipe up, then our routine to wipe up milk might not take in a Towel, bur rather a Wipe-Liquid Interface. Thus you could pass in a paper towel, cloth towel, or even your shirt as long as they all implement the Wipe Liquid Interface.

Let’s look at encapsulation and its fraternal twin, information-hiding. Trying to define these separately gets confusing, and in fact they are often used synonymously.  We group things that logically go together in a construct that separates the internal details from its external interface. Grouping these things together is essentially encapsulating them, but hiding the details completes true object-oriented encapsulation. So why do we do this?  Oh you know the answer… to help separate concerns! If I have a User object and it contains a Password, no one outside of the User object need concern themselves with the makeup of that password. Is it a string? Is it numerical? Is it encrypted? Back in OO land we encapsulate things inside classes and we hide everything about that thing so no one has to be burdened with the knowledge, no one has to be concerned with that information except the class. Like always the benefits are that the code will be easier to think, work, understand and communicate about. The user object does not provide access to its internal structure, but instead provides methods so that if calling code has a question, the user can answer it. For example, we may decide that the purpose of a User object is to validate that a person can gain access to the system. The user object might expose a method called CanUserGainAccess that takes in a string password. The user object compares that to the password string it has and then checks to make sure the user is not disabled. If all is cool it returns true. We decide later that passwords should be encrypted. So we go to the user class and change the logic for determining access to encrypt the given password before it is compared. No code outside the user class has to change. We are sure about this because the password is hidden behind the class.

Some mistakenly think they are providing data encapsulation by not exposing data behind a class directly but rather indirectly via an accessor/getter method. So instead of a class having a public member variable like Password, they write a getter method that returns a reference to the member variable. However, this buys us nothing. For example, when a password that was an integer changes to a string any code using it will now bomb. God forbid the Password property was public, it might take quite a while to find all the places where it will bomb. Also, where is all the logic that makes decisions based on user information? We can’t say in the User object, because with its data exposed anyone might have been getting the data and making some decision based on it.

In the end, all I hope to get across by the mental “The Separation of Concerns” sign in my head is that any design choice is made better by applying this credo. You can apply this in any language, object-oriented or not. However, the beauty of an object oriented-language is that it is directly geared towards helping you do this.

References: Tell, Don’t Ask

Procedural code gets information then makes decisions. Object-oriented code tells objects to do things.  — Alec Sharp

No comments:

Post a Comment