Thursday, March 3, 2011

Single Responsibility Principle (SRP)

The Single Responsibility Principle.
  • Each responsibility should be a separate class, because each responsibility is an axis of change.
  • A class should have one, and only one, reason to change.
  • If a change to the business rules causes a class to change, then a change to the database schema, GUI, report format, or any other segment of the system should not force that class to change.
So in brief, principle states: There should never be more than one reason for a class to change. Below is an example of some code breaking this principle.

Case 1 : Violating SRP


 
public class WeatherDisplay
{
private int _Temperature;
private int _WindSpeed;

public WeatherDisplay(int temperature, int windSpeed)
{
_Temperature = temperature;
_WindSpeed = windSpeed;
}

public void DisplayWeather()
{
DisplayTemperature();
DisplayWindSpeed();
}

private void DisplayTemperature()
{
println("The current temperature is: " + _Temperature + " degrees celcius.");
}
private void DisplayWindSpeed()
{
println("The current wind speed is: " + _WindSpeed + " meters per second.");
}
}





As you can see there are 2 distinct responsibilities for the class WeatherDisplay (you could even say there are 3 responsibilities). You could say one is to display the temperature and the other is to display the wind speed. Now because we are talking about 2 responsibilities we can also say that there are at least 2 reasons why we would / could change this class in the future, the temperature should be displayed in Fahrenheit and the wind speed should be displayed in knots.

Case 2 : Following SRP


Below we see how we could solve this and be coding according to the Single Responsibility Principle.
WeatherDisplay

public class WeatherDisplay
{
private DisplayTemperature _DisplayTemperature;
private DisplayWindSpeed _DisplayWindSpeed;

public WeatherDisplay(int temperature, int windSpeed)
{
_DisplayTemperature = new DisplayTemperature(temperature);
_DisplayWindSpeed = new DisplayWindSpeed(windSpeed);
}

public void DisplayWeather()
{
_DisplayTemperature.Display();
_DisplayWindSpeed.Display();
}
}
}

TemperatureDisplay

public class DisplayTemperature
{
private int _Temperature;

public DisplayTemperature(int temperature)
{
_Temperature = temperature;
}

public void Display()
{
println("The current temperature is: " +
_Temperature + " degrees celcius.");
}
}

WindSpeedDisplay
public class DisplayWindSpeed
{
private int _WindSpeed;

public DisplayWindSpeed(int windSpeed)
{
_WindSpeed = windSpeed;
}

public void Display()
{
println("The current wind speed is: " +
_WindSpeed + " meters per second.");
}
}

In this second example you can see that I now re-factored the class WeatherDisplay into three classes. Weather Display still exists but now it is using the class TemperatureDisplay and WindSpeedDisplay to actually display the data.

 

Case 3 : Better implementation


As I said before you could even say that there were 3 responsibilities for the first example and they would be displaying the information, providing temperature text markup and providing wind speed text markup. So in that case we would probably re-factor our example in the following way:

Interface: ITextDisplay

public interface ITextDisplay  {
void Display(TextWriter writer);
}
DisplayTemperature

public class TemperatureDisplay implements ITextDisplay
{
private int _Temperature;

public TemperatureDisplay(int temperature)
{
_Temperature = temperature;
}

public void Display(TextWriter writer)
{
writer.WriteLine("The current temperature is: " + _Temperature + " degrees celcius.");
}
}

DisplayWindSpeed


public class WindSpeedDisplay implements ITextDisplay
{
private int _WindSpeed;

public WindSpeedDisplay(int windSpeed)
{
_WindSpeed = windSpeed;
}

public void Display(TextWriter writer)
{
writer.WriteLine("The current wind speed is: " + _WindSpeed + " meters per second.");
}
}

As you can see now instead of providing the class WeatherDisplay with a fixed number of parameters we can now provide it with an unlimited amount of ITextDisplay implementations. The class WeatherDisplay is now responsible for providing a proper TextWriter and telling each ITextDisplay implementation to display its information. The ITextDisplay implementations now only have to write its information to the provided writer, and thus also implementing the Liskov Substitution Principle.



No comments:

Post a Comment

Chitika