Applied Abstraction (Part 1)

As part of my new blog, I am going to post a series of items related to applying abstraction. Heres my first part.

Building software is an exercise in complexity management. It’s not the only objective, but it’s a very important one nonetheless. In fact throughout our day we all come into contact with objects and processes that are the product of complexity management. Your TV remote manages the complexity of how it functions by hiding it behind a user interface which you use to control it. Your microwave, mobile phone, car and any other device is often a finely tuned expression of hiding the important but messy details away from its users. A modern car is a wonderful example of hiding complexity behind simple and intuitive controls. My car has a Dynamic Stability and Traction Control (DSTC) system to help stop my car from skidding off the road that works in conjunction with the Roll Stability Control (RSC) which prevents my car from rolling during an evasive manoeuvre. Imagine if I needed to understand how the DSTC works just so I could drive my car safely. I would need to understand how the cars electronic wheel monitor checks for individual movements on each of the wheels, and I would need to know how the system applies its brakes to the four wheels to different degrees to recover from a skid. Similarly I would need to know how the DSTC works with the RSC – how would I ever enjoy driving my car! Fortunately all of this complexity is hidden away from me. I have a button I press on my dashboard to engage or disengage the DSTC, but I don’t need to know what happens behind the scenes. When I upgrade my car I can enjoy the same benefits of complexity management as before, because I won’t have to learn the proprietary safety systems of a different car manufacturer.

The same well-established design principles applied by modern product manufacturing apply equally well to the practice of software construction, because for both complexity management is an exercise in abstraction. Abstraction allows details to be represented by a higher concept, and it is this concept with which the end-user interacts. In software, abstraction as a process is the identification and classification of the higher concepts from the problem domain. Abstraction is a wonderful stabilizer, because the end-user depends on a construct that by definition is devoid of implementation details, and it is often these details that change more than the construct itself. I might upgrade my DVD player to the new model of the same brand, and the manufacturer may have improved the remote control electronics. To me however, the remote control might look exactly the same as the old one. I as the end-user depend on the construct of the remote control, not the implementation details hidden behind it. This same principle applied to software construction results in more stable and flexible code. For this reason abstraction is one focus for this discussion.

Separation of concerns primarily helps to create cohesive modules, such as a method, a class or namespace. We can create suitably abstract types, yet achieve poor separation of concerns within them. Abstraction is a tool to create abstract specifications out of details, and separation of concerns allows these details to be distributed appropriately across the classes and interfaces that make up the software. These concerns are themselves the product of abstraction so that the software elements that eventually call onto them are protected from the internal details.

Notice in the code in listing A how the Analyser class is traversing over the structure of the AnalysisRun class to finally arrive at the Result objects to test if they match the analyteName that is to be used during the AnalyseResults method. The Analyser is coupled to the structure of the AnalysisRun when all it really requires is a list of Result objects to analyse against. Should this structure need to change in the AnalysisResults area, so will the Analyser because it is overly concerned with details which the designer overlooked.

public class Analyser {

    public Analyser(AnalysisRun run) {
        _run = run;
    }

    public void Analyse(string analyteName) {
        ArrayList results = new ArrayList();
        for (int i = 0; i < run.InjectionCount; i++) {
            Injection injection = run.GetInjection(i);
            for (int j = 0; j < injection.ResultCount; j++) {
                Result result = injection.GetResult(i);
                if (result.Matches(analyteName)) {
                    results.Add(result);
                }
            }
        }
        AnalyseResults(results);
    }

    private void AnalyseResults(ArrayList results) {
            // ...
    }

    private AnalysisRun _run;

}

Listing A

Using abstraction we can identify the essence of the nested for-loops – to extract a number of Result objects that match the analyteName. These traversal concerns exist in the wrong place, and we can change this by separating them from the Analyser class completely, as shown below:

 

public class Analyser {
    public void Analyse(string analyteName) {
        AnalyseResults(_run.GetResultsFor(analyteName));
    }

    private void AnalyseResults(ArrayList results) {
        // …
    }
    private AnalysisRun _run;
}

Listing B

The traversal concerns have been pulled out of the Analyser class and relocated onto the AnalysisRun class, where they belong. GetResultsFor is itself an abstract construct, hiding away the traversal details from any client of the AnalysisRun class. This simple step has improved cohesion of the Analyser and made it more stable from its reduced exposure to details in the AnalysisRun class, and the AnalysisRun class manages its own data with greater information hiding, which again helps to deal with complexity management.

For the rest of this discussion we will walk through a real-world system and perform a code-review, focusing on abstraction and separation of concerns as we do so. In the first section I go to great lengths to explain what the issues are with the code, and in the last section I take the code through a number of refactorings to introduce how some of the design principles I have already mentioned go into creating better designs.

end of part 1.

Advertisements
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: