Single Responsibility Principle in C# – SOLID Design Principles – Part 1

Posted on Updated on


Overview

In our introduction to the SOLID Design Principles, we mentioned the Single Responsibility Principle as one of the five principles specified. In this post we are going to dive into this design principle with a very simple example in C#.

The Single Responsibility Principle states that a class should have only one reason for change. The greater the number of responsibilities, the more reasons a class will have for change. Seems pretty simple, right? When you consider it for what it is, it is pretty simple.

I am most definitely NOT a mechanic, and I do not claim to know a great deal about combustion engines, but I think that a subset of an engine’s functionality is a great way to illustrate the SOLID design principles. The engine in your automobile is a marvel of modern engineering and has been designed to function optimally with each component having minimal dependencies on other components. An engine is maintainable because the various parts/components are easily removed and replaced. This is how our applications should be written.

So let’s consider an automobile engine from the standpoint of the starter mechanism. In case you don’t know, your engine has a component called a starter that is attached to the engine, has electrical connections to allow it to draw power from the battery, and when you engage your ignition switch via a key or pushbutton, the starter is energized. When it is energized, it forces the engine to turn over and the combustion process begins. If you would like to learn more about how a starter works, here is a great article πŸ™‚ Haha. So let’s move on, shall we?

IMPORTANT: For the sake of simplicity, we are going to assume that for this example, the one hard rule that will not ever change is the fact that there will always be a Starter object and a Battery object associated with an Engine. If we don’t make this assumption and declare it as a “rule”, the scope of our design changes could make the illustration of the concept overly-complicated and I really want to keep it simple here and discuss the principle in the simplest terms possible.

Furthermore we are going to use our design in this post to continue to apply SOLID design principles one by one.

Design #1 – Not so Good

Let’s suppose that we wanted to represent an Engine’s ignition/starter functionality in a few C# classes. If we didn’t understand the Single Responsibility Principle, we might build our classes similarly to this:

Engine Ignition Initial (Invalid) Class Design - Single Responsibility Principle

Based on the design shown above, let’s consider this code:

public class Engine
{
    public IgnitionResult Start(Starter starter, Battery battery)
    {
        //we would put code here to handle the logic for checking
        //whether or not the batter is charged

        //then we check the result of our logic
        if (battery.IsCharged)
        {
            //we could put logic here to handle
            //the actual ignition process
            return IgnitionResult.Success;
        }
        else
        {
            //uh oh! the battery is not charged
            //Failure!
            return IgnitionResult.Failure;
        }
    }
}

public class Starter
{
    public string Brand
    {
        get;
        set;
    }

    public string Model
    {
        get;
        set;
    }
}

public class Battery
{
    public string Brand
    {
        get;
        set;
    }

    public string Model
    {
        get;
        set;
    }

    public bool IsCharged
    {
        get;
        set;
    }
}

public enum IgnitionResult
{
    Success,
    Failure
}

Let’s think about this code as it is written. It makes sense that we would have an Engine class, a Starter class, a Battery class, and an IgnitionResult enum. So far so good. But when we look in the Engine class and read the Start() method, we can see that there may be more than one reason why the Engine class would have to be changed. It is responsible for too many things. Any logic associated with how to start the engine is contained within the Start() method as is the “validation” of determining whether or not the battery is charged.

Consider the following questions:

  1. What if we installed a different type of Battery and the logic associated with verifying its charge state changed?
  2. What if we installed a different type of Starter and the logic associated with how it actually works internally changed?

If either of these things changed we would have to modify our Engine class to accommodate the change(s). The key point here is that the Engine class has more than one responsibility and per the Single Responsibility Principle this is not good.

Design #2 – Better!

Now let’s reconsider our design, remembering that each class should have not more than one reason for change.

First, the logic for actually handling the Starter’s ignition process should be moved to the Starter class and the Starter itself should contain a Start() method which will be invoked by the Engine’s Start() method.

Next, the battery charge validation logic should be moved to the Battery class since the battery itself knows better than anything how to validate its own state. Sounds sensible, right?

Let’s take a look at the improved design:

Single Responsibility Principle - SOLID Design Principles - Improved design

So what did we do? First, we removed anything to do with the “internal workings” of the Starter and the Battery out of the Engine class and into each respective class. Now, based on the assumption we made above that stated in this scenario an Engine will always have exactly one Starter and exactly one Battery, the Engine class has only one reason for change as do the Starter and Battery classes.

Keep in mind that as we get into the other SOLID Design Principles we are going to begin abstracting things so that we will have a truly “pluggable” design but for now we are working directly with concrete Starter and Battery objects. That will change as we move through the other principles and we will begin to see continuous improvement!

We left the Brand and Model properties in the Starter and Battery classes. Obviously we can see that this is not an ideal design, but remember – we are focusing on the Single Responsibility Principle for now! These properties are inconsequential now.

Based on the design shown above, our new code looks like this:

public class Engine
{
    public IgnitionResult Start(Starter starter, Battery battery)
    {
        return starter.Start(battery);
    }
}

public class Starter
{
    public string Brand
    {
        get;
        set;
    }

    public string Model
    {
        get;
        set;
    }

    public IgnitionResult Start(Battery battery)
    {
        //since the Battery class now contains that actual charge validation
        //logic, the Starter merely checks the value of that property
        //and the Battery takes care of the rest
        if (battery.IsCharged)
        {
            //we can put the ignition logic here
            return IgnitionResult.Success;
        }
        else
        {
            return IgnitionResult.Failure;
        }
    }
}

public class Battery
{
    public string Brand
    {
        get;
        set;
    }

    public string Model
    {
        get;
        set;
    }

    public bool IsCharged
    {
        get
        {
            //we can put logic here for the battery
            //to validate its charge and return
            //a result
            return true;
        }
    }
}

public enum IgnitionResult
{
    Success,
    Failure
}

So we have a better design from the standpoint of the Single Responsibility Principle. The goal is to modify all of our classes so that each class has one and only one reason for change. Since the example is very simple, accomplishing this is pretty easy. As designs become more complex, the amount of time to create the correct design can grow tremendously but the time required is very well worth it long-term because it will yield a much more maintainable and effective design overall.

In the next post, we will dive into the Open-Closed Principle. See the links below for all posts related to the SOLID Design Principles.

The SOLID Design Principles
The Single Responsibility Principle (SRP)
The Open-Closed Principle (OCP)
The Liskov Substitution Principle (LSP)
The Interface Segregation Principle (ISP)
The Dependency Inversion Principle (DIP)

One thought on “Single Responsibility Principle in C# – SOLID Design Principles – Part 1

    […] https://johnlnelson.com/2014/07/20/single-responsibility-principle-in-c-solid-design-principles/ – a more sophisticated example of design without SRP and design with SRP […]

    Like

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