Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

SOLID Principles by Examples: Single Responsibility

DZone's Guide to

SOLID Principles by Examples: Single Responsibility

In this post, you'll learn about the first SOLID principle of programming, the Single Responsibility Principle, to write high-cohesion, low-coupling code.

· DevOps Zone ·
Free Resource

The need for DevOps innovation has never been greater. Get the results from over 100 business value assessments in this whitepaper, Digital Darwinism: Driving Digital Transformation, to see the positive impact of DevOps first hand.

This blog post will explain with simple examples the Single Responsibility Principle of SOLID principles to better understand how we can improve our daily coding activities. In future posts, I'll cover the other four principles.

The Definition

"A class should have only one reason to change."

Defined by Robert C. Martin in his book "Agile Software Development, Principles, Patterns, and Practices," it is the first of the five SOLID principles. What it states is very simple, however, achieving that simplicity is not so obvious.

Let's start with the classic example, an object that can save itself.

class Book_NoSRP
    {
        public string Author { get; set; }

        public string Title { get; set; }

        public void Save()
        {
            //Save to database code.
            //Open db connection.
            //Make some logic checks.
            //DB CRUD operations.
            //Close connection.
        }
    }

This class violates the SRP principles because it has more than one reason to change:

  • It can change because we want to add a specific attribute for a book- the year of the first edition, for example.
  • It can change because our database structure changes and we need to update the data operations, or because we want to save our book in an XML file format.

How do we change this situation?

We move the saving logic away from this class:

class Book_SRP
    {
        public string Author { get; set; }

        public string Title { get; set; }
    }

    class PersistenceService
    {
        public void Save(Book_SRP bookToSave)
        {
            //Save to database code.
            //Open db connection.
            //DB CRUD operations.
            //Close connection.
        }
    }

This principle can help us to design better procedures or functions, too. This method, for example, is doing too much. Because of that, the SendAlert class could change if the validation criteria changes, and that is not the main purpose of the class.

class AlertService
    {

public void SendAlert(string emailTo, string alertMessage)
        {
            if (!emailTo.Contains("@"))
            {
                throw new ArgumentException("email is not in a valid format");
            }

            if (!emailTo.Contains("blackListedDomain.com"))
            {
                throw new InvalidOperationException("domain blackListedDomain.com blackListed.");
            }

            if (alertMessage == null)
            {
                throw new ArgumentNullException(nameof(alertMessage));
            }

            SmtpClient client = new SmtpClient();
            client.Send(new MailMessage("mysite@nowhere.com", emailTo)
            {
                Subject = "Alert",
                Body = alertMessage
            });
        }
}

We can refactor like this.

interface IEmailValidationService
    {
        string Validate(string emailTo);
    }

class AlertService
    {
        private readonly IEmailValidationService _emailValidationService;

        public AlertService(IEmailValidationService emailValidationService)
        {
            _emailValidationService = emailValidationService;
        }

        public void SendAlert(string emailTo, string alertMessage)
        {
            var validationError = _emailValidationService.Validate(emailTo);

            if (!string.IsNullOrWhiteSpace(validationError))
            {
                throw new InvalidOperationException(validationError);
            }

            if (alertMessage == null)
            {
                throw new ArgumentNullException(nameof(alertMessage));
            }

            SmtpClient client = new SmtpClient();
            client.Send(new MailMessage("mysite@nowhere.com", emailTo)
            {
                Subject = "Alert",
                Body = alertMessage
            });
        }
    }

This way, the AlertService class has only one reason to change, and it doesn't have to change if something in the validation logic changes.

TL;DR

The primary benefit the Single Responsibility Principle gives you is high-cohesion, low-coupling code. Following the SRP minimizes the chance that one class will have to change for a given requirement, and maximizes the possibility that changing one class will not impact any other classes.

Happy coding!

Click here for the next part of this series: Open/Closed!

Interested in Kubernetes but unsure where to start? Check out this whitepaper, A Roundup of Managed Kubernetes Platforms from Codeship by Cloudbees, for an overview and comparison of Kubernetes platforms. 

Topics:
solid principles ,devops ,agile ,solid ,programming

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}