The Single Responsibility Principle (SRP) is a SOLID principle of object-oriented design. It states that a class should have only one reason to change. In other words, a class should have only one responsibility or job.
It makes coffee
Here’s an example of a class in C# that represents a coffee machine and it’s process:
class CoffeeMachine
{
public void MakeCoffee(string type)
{
Console.WriteLine("Making a cup of " + type + " coffee.");
Console.WriteLine("Grinding coffee beans...");
Console.WriteLine("Boiling water...");
Console.WriteLine("Pouring coffee into cup...");
Console.WriteLine("Enjoy your " + type + " coffee!");
}
}
The MakeCoffee
method takes a parameter type
of type string
, which can be used to specify the type of coffee to make (e.g. « black » or « latte »). The method then outputs messages to the console to simulate the process of making a cup of coffee, such as grinding coffee beans, boiling water, pouring the coffee into a cup, and adding cream and sugar.
Here’s an example of how you can use the CoffeeMachine
class in your program:
class Program
{
static void Main(string[] args)
{
var coffeeMachine = new CoffeeMachine();
coffeeMachine.MakeCoffee("black");
}
}
Here, we create the object of the CoffeeMachine
class and call the MakeCoffee
method with string argument « black » to simulate the process of making black coffee.
The job is done. It make coffee. But it’s so far to respect the Single Responsibility Principle.
Divide and rule
The Single Responsibility Principle (SRP) states that a class should have only one reason to change, which means that a class should only have one responsibility. In the example class that I provided earlier, the CoffeeMachine
class has multiple responsibilities.
Here’s an example of how you could split the process of making coffee and adhere to the Single Responsibility Principle:
class CoffeeBeansGrinder
{
public void GrindCoffeeBeans()
{
Console.WriteLine("Grinding coffee beans...");
}
}
class WaterBoiler
{
public void BoilWater()
{
Console.WriteLine("Boiling water...");
}
}
class Pourer
{
public void PourCoffee(string type)
{
Console.WriteLine("Pouring " + type + " coffee into cup...");
}
}
class CoffeeMachine
{
private readonly CoffeeBeansGrinder _coffeeBeansGrinder;
private readonly WaterBoiler _waterBoiler;
private readonly Pourer _pourer;
public CoffeeMachine()
{
_coffeeBeansGrinder = new CoffeeBeansGrinder();
_waterBoiler = new WaterBoiler();
_pourer = new Pourer();
}
public void MakeCoffee(string type)
{
Console.WriteLine("Making a cup of " + type + " coffee.");
_coffeeBeansGrinder.GrindCoffeeBeans();
_waterBoiler.BoilWater();
_cup.PourCoffee(type);
Console.WriteLine("Enjoy your " + type + " coffee!");
}
}
This time we have four classes CoffeeBeansGrinder
, WaterBoiler
, Pourer
and CoffeeMachine
class. Each class has a single responsibility:
CoffeeBeansGrinder
is responsible for grinding coffee beans.WaterBoiler
is responsible for boiling water.Pourer
is responsible for pouring coffee into a cup.CoffeeMachine
is responsible for coordinating the process of making coffee, by making use of these three classes.
The CoffeeMachine
class creates an instance of each of these classes, and then makes use of them in the MakeCoffee
method.
This way it only calls the function of each class which is defined in its respective class and has a single responsibility By splitting the responsibilities in this way, we have made the classes more focused and easier to understand and maintain.
If we need to change the way a specific part of the coffee making process works, we can make the changes to the corresponding class without affecting the other parts of the process.
Final Thoughts
In this technical, we have discussed the importance of the Single Responsibility Principle (SRP) in software design and development. We have shown how adhering to SRP can lead to more maintainable and flexible code, by making each class focused on a single responsibility.
Using the example of a coffee machine class, we have demonstrated how to refactor an initial implementation that did not adhere to SRP, into a design that follows SRP. We split the class into several classes, each with a single responsibility. This way, the code is more focused, easier to understand, and maintainable.
To implement SRP in your code, it is crucial to clearly define the responsibility of each class, and identify and split classes that have multiple responsibilities. Best practices include having small, focused and cohesive classes.
It is worth mentioning that as the codebase grows, the complexity and the number of classes increases, so it’s important to keep an eye on performance and testability.
Overall, adhering to the SRP is an important aspect of software design and development, and can lead to more maintainable and flexible code.