Agile Zone is brought to you in partnership with:

John Sonmez is a Pluralsight author of over 25 courses spanning a wide range of topics from mobile development to IoC containers. He is a frequent guest on podcasts such as DotNetRocks and Hanselminutes. John has created applications for iOS, Android and Windows Phone 7 using native tools, HTML5, and just about every cross platform solution available today. He has a passion for Agile development and is engaged in a personal crusade to make the complex simple. John is a DZone MVB and is not an employee of DZone and has posted 83 posts at DZone. You can read more from them at their website. View Full User Profile

Top Posts of 2013: There Are Only 2 Roles of Code

12.30.2013
| 90239 views |
  • submit to reddit
All code can be classified into two distinct roles; code that does work (algorithms) and code that coordinates work (coordinators).

The real complexity that gets introduced into a code bases is usually directly related to the creation of classes that group together both of these roles under one roof.

I’m guilty of it myself.  I would say that 90% of the code I have written does not nicely divide my classes into algorithms and coordinators.

Defining things a bit more clearly

Before I dive into why we should be dividing our code into clear algorithmic or coordinating classes, I want to take a moment to better define what I mean by algorithms and coordinators.

Most of us are familiar with common algorithms in Computer Science like a Bubble Sort or a Binary Search, but what we don’t often realize is that all of our code that does something useful contains within it an algorithm.

What I mean by this is that there is a clear distinct set of instructions or steps by which some problem is solved or some work is done.  That set of steps does not require external dependencies, it works solely on data, just like a Bubble Sort does not care what it is sorting.

Take a moment to wrap your head around this.  I had to double check myself a couple of times to make sure this conclusion was right, because it is so profound.

It is profound because it means that all the code we write is essentially just as testable, as provable and potentially as dependency free as a common sorting algorithm if only we can find the way to express it so.

What is left over in our program (if we extract out the algorithms) is just glue.

Think of it like a computer.  Computer electronics have two roles: doing work and binding together the stuff that does the work.  If you take out the CPU, the memory and all the other components that actually do some sort of work, you’ll be left with coordinators.  Wires and busses that bind together the components in the system.

Why dividing code into algorithms and coordinators is important.

So now that we understand that code could potentially be divided into two broad categories, the next question of course is why?  And can we even do it?

Let’s address why first.

The biggest benefit to pulling algorithmic code into separate classes from any coordinating code is that it allows the algorithmic code to be free of dependencies.  (Practically all dependencies.)

Once you free this algorithmic code of dependencies you’ll find 3 things immediately happen to that code:

  1. It becomes easier to unit test
  2. It becomes more reusable
  3. Its complexity is reduced

A long time ago before mocks were widely used and IoC containers were rarely used, TDD was hard.  It was really hard!

I remember when I was first standing on the street corners proclaiming that all code should be TDD with 100% code coverage.  I was thought pretty crazy at the time, because there really weren’t any mocking frameworks and no IoC containers, so if you wanted to write all your code using TDD approaches, you’d actually have to separate out your algorithms.  You’d have to write classes that had minimal dependencies if you wanted to be able to truly unit test them.

Then things got easier by getting harder.  Many developers started to realize that the reason why TDD was so hard was because in the real world we usually write code that has many dependencies.  The problem with dependencies is that we need a way to create fake versions of them.  The idea of mocking dependencies became so popular that entire architectures were based on the idea and IoC containers were brought forth.

MP900175522We, as a development community, essentially swept the crumbs of difficult unit testing under the rug.  TDD and unit testing in general became ubiquitous with writing good code, but one of the most important values of TDD was left behind, the forced separation of algorithmic code from coordinating code.

TDD got easier, but only because we found a way to solve the problems of dependencies interfering with our class isolation by making it less painful to mock out and fake the dependencies rather than getting rid of them.

There is a better way!

We can still fix this problem, but we have to make a concerted effort to do so.  The current path of least resistance is to just use an IoC container and write unit tests full of mocks that break every time you do all but the most trivial refactoring on a piece of code.

Let me show you a pretty simple example, but one that I think clearly illustrates how code can be refactored to remove dependencies and clearly separate out logic.

Take a look at this simplified calculator class:

public class Calculator
   {
       private readonly IStorageService storageService;
       private List<int> history = new List<int>();
       private int sessionNumber = 1;
       private bool newSession;
 
       public Calculator(IStorageService storageService)
       {
           this.storageService = storageService;
       }
 
       public int Add(int firstNumber, int secondNumber)
       {
           if(newSession)
           {
               sessionNumber++;
               newSession = false;
           }
 
           var result = firstNumber + secondNumber;
           history.Add(result);
 
           return result;
       }
 
       public List<int> GetHistory()
       {
           if (storageService.IsServiceOnline())
               return storageService.GetHistorySession(sessionNumber);
 
           return new List<int>();
       }
 
       public int Done()
       {
           if (storageService.IsServiceOnline())
           {
               foreach(var result in history)
                   storageService.Store(result, sessionNumber);
           }
           newSession = true;
           return sessionNumber;
       }
   }

This class does simple add calculations and stores the results in a storage service while keeping track of the adding session.

It’s not extremely complicated code, but it is more than just an algorithm.  The Calculator class here is requiring a dependency on a storage service.

But this code can be rewritten to extract out the logic into another calculator class that has no dependencies and a coordinator class that really has no logic.

public class Calculator_Mockless
   {
       private readonly StorageService storageService;
       private readonly BasicCalculator basicCalculator;
 
       public Calculator_Mockless()
       {
           this.storageService = new StorageService();
           this.basicCalculator = new BasicCalculator();
       }
 
       public int Add(int firstNumber, int secondNumber)
       {
           return basicCalculator.Add(firstNumber, secondNumber);
       }
 
       public List<int> GetHistory()
       {
           return storageService.
                  GetHistorySession(basicCalculator.SessionNumber);
       }
 
       public void Done()
       {
           foreach(var result in basicCalculator.History)
               storageService
                    .Store(result, basicCalculator.SessionNumber);
 
           basicCalculator.Done();
       }
   }
 
   public class BasicCalculator
   {
       private bool newSession;
 
       public int SessionNumber { get; private set; }
 
       public IList<int> History { get; private set; }
 
       public BasicCalculator()
       {
           History = new List<int>();
           SessionNumber = 1;
       }
       public int Add(int firstNumber, int secondNumber)
       {
           if (newSession)
           {
               SessionNumber++;
               newSession = false;
           }
 
           var result = firstNumber + secondNumber;
           History.Add(result);
 
           return result; ;
       }
 
       public void Done()
       {
           newSession = true;
           History.Clear();
       }
   }

Now you can see that the BasicCalculator class has no external dependencies and thus can be easily unit tested.  It is also much easier to tell what it is doing because it contains all of the real logic, while the Calculator class has now become just a coordinator, coordinating calls between the two classes.

This is of course a very basic example, but it was not contrived.  What I mean by this is that even though this example is very simple, I didn’t purposely create this code so that I could easily extract out the logic into an algorithm class.

Parting advice

I’ve found that if you focus on eliminating mocks or even just having the mindset that you will not use mocks in your code, you can produce code from the get go that clearly separates algorithm from coordination.

I’m still working on mastering this skill myself, because it is quite difficult to do, but I believe the rewards are very high for those that can do it.  In code where I have been able to separate out algorithm from coordination, I have seen much better designs that were more maintainable and easier to understand.

I’ll be talking about and showing some more ways to do this in my talk at the Warm Crocodile conference next year.

Published at DZone with permission of John Sonmez, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Saulius Narkevicius replied on Wed, 2013/03/27 - 2:35pm

That is a nice separation of roles (or responsibilities). But aren't two roles a limitation? The popular Single Responsibility Principe ('S' out of S.O.L.I.D. design principles) states, that a class must have single responsibility anyway: be it calculating, history tracking, DB access, XML parameter parsing before passing them to the method, etc..

Have you considered comparing 2 roles of code to S.O.L.I.D. design principles?


David Fu replied on Thu, 2013/04/11 - 1:20pm

and also data model(POJO)

Joe Ge replied on Thu, 2013/04/11 - 2:14pm

I think I have written and/or seen a lot of code at or near the database level that is not Control/Coordination (see DARTS for definition) nor algorithm.  So while some of your post is interesting, the mathematician in says that your premise has been proven false by counter-example.  Thus there are more than 2 roles....

Balázs Bessenyei replied on Tue, 2013/05/14 - 9:54am


Actually you have not eliminated the Mocks. Calculator_Mockless has 2 external dependencies, the BasicCalculator and the StorageService.
Both of these needs to be mocked in order to verify the logic of the Calculator_Mockless class.
The only thing you have achieved is separating the Storage and Calculation logics/concerns (Single Responsibility Principle from SOLID).
And introduced a mediator/facade to coordinate the separated concerns. But you still have to deal with the mocks in the coordinating class.

John Sonmez replied on Tue, 2013/05/14 - 10:01am in response to: Balázs Bessenyei

There is no need to test the coordinating classes, since they contain no real logic now.

Jorge Castro replied on Mon, 2013/05/27 - 12:49pm

It start bad when you talk about unit test.  Unit Test (most of the time) IS USELESS.

Thirdy De Rivera replied on Thu, 2013/07/25 - 11:47pm

This google tech talk by Miško Hevery, provides more into this topic. Note that he used an IoC framework "Google Guice" to greatly reduce if not eliminate the "coordination" work via plain Java Annotations. Therefore, almost only the "algorithm" code is left.

Design Tech Talk Series Presents: OO Design for Testability 

http://www.youtube.com/watch?v=acjvKJiOvXw

I would also recommend this book called "Effective Unit Testing"


Thirdy De Rivera replied on Thu, 2013/07/25 - 11:44pm in response to: John Sonmez

 I believe there is. In real-life experience, using the example provided, the call to History.Clear() can be missed.

If I am not mistaken, this is called IntegrationTest

Balázs Bessenyei replied on Sun, 2013/07/28 - 5:09pm in response to: John Sonmez

Yes they do, you can believe that they don't, but wait till reality proves you otherwise.

Ravin Sharma replied on Mon, 2013/10/07 - 8:28am in response to: Jorge Castro

Why?

joe to replied on Thu, 2013/11/07 - 8:41am

I remember a book from Niklaus Wirth titled Programs = Data Structures + Algorithms

Tuan Nguyen replied on Mon, 2013/11/11 - 12:46pm

What the heck is TDD? People always assume that anyone who reads this article would know what the heck is TDD! very annoying!

Andrew Steitz replied on Fri, 2013/12/13 - 12:23pm in response to: Tuan Nguyen

Really?  Type TDD into a search engine.  If you are using Chrome highlight it, right-click, and choose "Search Google for "TDD"". (Hint: it is not the Target Date Fund at the top)

Andrew Steitz replied on Fri, 2013/12/13 - 12:24pm in response to: Jorge Castro

I will make sure to never hire you.

Joe Ge replied on Sun, 2014/01/05 - 6:52pm in response to: Andrew Steitz

 This article and entire thought process shows a complete lack of understanding of software development in the real world.  That it might be considered a 'Top Post' is sad at too many levels to describe.  Oh wait, my bad - clearly Mr Sonmez has simply learned more than Wirth, DeMarco, Yourdon, Dijkstra, Booch, Rambaugh, and Jacobson...

Monu Yogi replied on Wed, 2014/02/19 - 2:09am

We can immobile difficulty this snag, still we contain to compose a concerted dint to do so.  The latest route of minimal opposition is to true hire an IoC bottle also scratch clan rehearses plump of jeers that shiver entire era you do many nevertheless the most slight refactoring on a crumb of canon.  Cell Tower Lease Rates

Chris Odell replied on Fri, 2014/04/25 - 10:31am

I like the idea that code can be split into 2 roles, algorithmic and coordination. However I am not convinced because I am not sure how data retrieval would fit, although I suppose you could say it is a coordination role?

It looks to me as though you have made the calculation example simpler than needed to emphasize your point which explains some of the comments above.

As for the BasicCalculator and StorageService I think that I would prefer to see interfaces for these classes and then use dependency injection to pass the interfaces in.

Shoeb Siddique replied on Mon, 2014/07/14 - 5:10pm

Thanks for sharing such a valuable piece of information. Very few people have art to write article beautifully and you are one of them. Literally, i like the way you represent the information. Can you give me some suggestion for my pnr status website.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.