Learning The S.O.L.I.D Programming Principles: 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 a one-shot. I divided this into following parts:

Introduction

In this whole article we will learn Single responsibility principle in details with example.

First of all lets revise what is SRP [ref. to Learning The S.O.L.I.D Programming Principles: Overview [Part – I]]?
“a class should not design to do multiple activities”

Learning Single responsibility principle (SRP)

Name of this principle described 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”. Lets dive into 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 in the following snippet:

	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
		}
	}
	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:
– Fetch data
– Process data
– then, Migrate 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? Lets start from name of class i.e. DataMigrater, this looks me to
class 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 – lets think a scenario where method public void Migrate(IEnumerable rawData){} throws
an exception and our data 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 want to bear this 🙂

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

	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 is having only one method Migrate(). You noticed there are lot of changes have been made in this class, we will discuss all one-by-one
in coming articles. As of now lets concentrate on Single Responsibility Principle.

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

See below:

	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);
		}
	}
	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:

	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
to take only one responsibility i.e. ‘Migrate data’.

How to download source-code?

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