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:
- S for SRP: Single responsibility principle
- O for OCP: Open/closed principle
- L for LSP: Liskov substitution principle
- I for ISP: Interface segregation principle
- 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:
public class DataMigrater
{
public IList<ServerData> GetData(DateTime initialDate, DateTime endDate)
{
return new List<ServerData>();
}
public IList<ServerData>ProcessData(IEnumerable<ServerData>rawData)
{
return rawData.ToList();
}
public void Migrate(IEnumerable<ServerData> rawData)
{
}
}
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();
var rawData = dataMigrater.GetData(StartDate, EndDate);
var processedData = dataMigrater.ProcessData(rawData);
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:
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
{
_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:
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();
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:
- S for SRP: Single responsibility principle
- O for OCP: Open/closed principle
- L for LSP: Liskov substitution principle
- I for ISP: Interface segregation principle
- 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.