Click here to Skip to main content
15,917,565 members
Articles / Programming Languages / C#

Learning The S.O.L.I.D Programming Principles: Single Responsibility Principle [Part - II]

Rate me:
Please Sign up or sign in to vote.
4.14/5 (6 votes)
26 Oct 2014CPOL3 min read 15K   18   9
Single Responsibility Principle (Part II)

History

In our previous ‘Learning The S.O.L.I.D. Programming Principles: Overview [Part - I]‘, we have learned all about S.O.L.I.D:

It is an acronym introduced by Michael Feathers as:

  1. S for SRP: Single responsibility principle
  2. O for OCP: Open/closed principle
  3. L for LSP: Liskov substitution principle
  4. I for ISP: Interface segregation principle
  5. D for DIP: Dependency inversion principle

This is a very vast topic and this is not possible to learn/explain in one-shot. I divided this into the following parts:

Introduction

In this whole article, we will learn Single responsibility principle in detail with an example.

First of all, let's revise what is SRP [ref. to Learning The S.O.L.I.D Programming Principles: Overview [Part - I]]?

Quote:

“a class should not design to do multiple activities

Learning Single Responsibility Principle (SRP)

Name of this principle describes itself, it should have single responsibility. Who should have single responsibility? Here, we are studying/learning principles to design best classes/programs/systems.

In reference to this, I would say “A class should have single responsibility”. Let's dive into the ocean – can we read this like “a class should not design to do multiple activities”, so, what kind of activities?

We already knew this: More, responsibility tied classes towards more changes in future.

Now, take a look at the following snippet:

C#
public class DataMigrater
{
    public IList<ServerData> GetData(DateTime initialDate, DateTime endDate)
    {
        //get server data within date range

        return new List<ServerData>();
    }

    public IList<ServerData>ProcessData(IEnumerable<ServerData>rawData)
    {
        //apply rules

        return rawData.ToList();
    }

    public void Migrate(IEnumerable<ServerData> rawData)
    {
        //migrate processedData from server to server
    }
}
C#
class Program
{
    private static readonly DateTime StartDate = new DateTime(2014, 07, 01);
    private static readonly DateTime EndDate = DateTime.Now;

    static void Main(string[] args)
    {
        var dataMigrater = new DataMigrater();

        //get raw data
        var rawData = dataMigrater.GetData(StartDate, EndDate);

        //process raw data
        var processedData = dataMigrater.ProcessData(rawData);

        //finally migrate processed data
        dataMigrater.Migrate(processedData);
    }
}

In our class, DataMigrater is having too many responsibilities. This class:

  • fetches data
  • processes data
  • then, migrates data

So, as per SRP, our class is doing wrong. Here, we are not following S.O.L.I.D. What should our class do? Let's start from name of class, i.e., DataMigrater, to me, this class looks like it should be responsible only to migrate data. So, class should not be concerned about what and how data is coming for migration.

One more reason, class should be responsible for one thing – let's think about a scenario where method public void Migrate(IEnumerable<serverdata> rawData){} throws an exception and our data does not get migrated. Now, we need to verify all three methods because we are not sure if data is well fetched or well processed. This laid us for many burdens.

Do you want to bear this load? I am sure, no developer wants to bear this. :)

Now, a big question is ‘how to do that?’ – take a look into the following snippet:

C#
public class DataMigrater
{
    private readonly IList<ServerData> _data;
    private readonly IServerDataRepository _repository;
    private readonly ILog _logger = LogManager.GetLogger(typeof(DataMigrater));

    public DataMigrater(IList<ServerData> data, IServerDataRepository repository)
    {
        _data = data;
        _repository = repository;
    }

    public void Migrate()
    {
        try
        {
            foreach (var data in _data)
            {
                var stopWatch = Stopwatch.StartNew();

                Migrate(data);

                stopWatch.Stop();

                _logger.InfoFormat("Total data {0} migrated in {1}", _data.Count, stopWatch.Elapsed);
            }
        }
        catch (Exception ex)
        {
            _logger.Error(ex);

            throw;
        }

    }
    private void Migrate(ServerData data)
    {
        try
        {
            //Migrate data from server to server

            _logger.InfoFormat("Migrating data with Id:{0}", data.Id);
        }
        catch (Exception ex)
        {
            _logger.ErrorFormat("An exception occurred attempting to migrate data with Id:{0}", data.Id);

            throw;
        }
    }
}

Now, our class has only one method Migrate(). You noticed that there are a lot of changes that have been made in this class, we will discuss all one-by-one in the coming articles. As of now, let's concentrate on Single Responsibility Principle.

In the above, our class is now only concerned with Migrate Server data. This class is not bothered about whether supplied data is processed or raw, there are other classes responsible for these things now.

See below:

C#
public class ServerProcessedOrRawDataQuery
{
    internal IServerDataRepository Repository { get; set; }
    public ServerProcessedOrRawDataQuery(IServerDataRepository repository)
    {
        Repository = repository;
    }
    public IQueryable<ServerData> Query(DateTime startDate, DateTime endDate)
    {
        return new ServerDataQuery(Repository).ProcessedData()
            .Where(d => d.InitialDate <= endDate && d.EndDate >= startDate);
    }
}
C#
public class ServerDataQuery
{
    internal IServerDataRepository Repository { get; set; }

    public ServerDataQuery(IServerDataRepository repository)
    {
        Repository = repository;
    }
    public IQueryable<ServerData> Query()
    {
        return Repository.Get().AsQueryable<ServerData>();
    }

    public IQueryable<ServerData> ProcessedData()
    {
        return Query().Where(d => d.IsDirty == false);
    }
}

Now, our console is changed as:

C#
class Program
{
    private static readonly DateTime StartDate = new DateTime(2014, 07, 01);
    private static readonly DateTime EndDate = DateTime.Now;

    static void Main(string[] args)
    {
        var repository = new ServerDataRepository(); //we can use any DI to initiate our repositories

        var processedData = GetProcessedData(repository);

        var dataMigrater = new DataMigrater(processedData,repository);

        Console.WriteLine("Data migration started...");

        dataMigrater.Migrate();

        Console.WriteLine("Data migration completed...");

        Console.ReadLine();
    }

    private static IList<ServerData> GetProcessedData(IServerDataRepository repository)
    {
        return new ServerProcessedOrRawDataQuery(repository).Query(StartDate, EndDate).ToList();
    }
}

Revisiting – Learning The S.O.L.I.D Programming Principles: Single responsibility principle [Part - II]

What We Learned

S.O.L.I.D. is an acronym introduced by Michael Feathers as:

  1. S for SRP: Single responsibility principle
  2. O for OCP: Open/closed principle
  3. L for LSP: Liskov substitution principle
  4. I for ISP: Interface segregation principle
  5. D for DIP: Dependency inversion principle

Single Responsible Principle says “a class should not design to do multiple activities”. In our referred example, we made our class take up only one responsibility, i.e., ‘Migrate data’.

How To Download the Source Code?

You can download the complete source code of examples used in this article from GitHub: Learning Solid.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer
India India
Learning never ends.

Comments and Discussions

 
SuggestionLinks Pin
ArchAngel12328-Oct-14 10:30
ArchAngel12328-Oct-14 10:30 
AnswerRe: Links Pin
Gaurav Aroraa28-Oct-14 20:59
professionalGaurav Aroraa28-Oct-14 20:59 
SuggestionI think you've misunderstood SRP Pin
ArchAngel12328-Oct-14 10:28
ArchAngel12328-Oct-14 10:28 
GeneralMy vote of 1 Pin
iRakeshJha28-Oct-14 5:32
professionaliRakeshJha28-Oct-14 5:32 
AnswerRe: My vote of 1 Pin
Gaurav Aroraa28-Oct-14 9:33
professionalGaurav Aroraa28-Oct-14 9:33 
BugSource code is missing Pin
Shuby Arora27-Oct-14 11:50
Shuby Arora27-Oct-14 11:50 
Where is source code?
AnswerRe: Source code is missing Pin
Gaurav Aroraa27-Oct-14 12:07
professionalGaurav Aroraa27-Oct-14 12:07 
AnswerRe: Source code is missing Pin
Shuby Arora30-Oct-14 11:31
Shuby Arora30-Oct-14 11:31 
AnswerRe: Source code is missing Pin
Gaurav Aroraa30-Oct-14 11:42
professionalGaurav Aroraa30-Oct-14 11:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.