Single Responsibility Principle (SRP)
The Single Responsibility Principle (SRP) can be described this way:
A module should have only one responsibility, and only one, reason to change.
Unfortunately, this sentence is difficult to understand without proper explanation.
- What does the word "module" mean?
The simple definition is a source file. We group together the functions that need to change for the same reason, and separate the things that change for other reasons.
- What does "reason to change" mean?
Software systems are changed to satisfy actors, usually they are called users and stakeholders. Actors are the "reason to change".
- Why should you follow SRP?
If a module has multiple responsibilities, there is a likelihood that it is used in a lot of places. When one responsibility is changed, not only do we run a higher risk of introducing defects into other responsibilities in the same class, but there is a greater number of other actors that might be impacted.
By following the single responsibility principle, if we need to change a particular responsibility, that change would be located in a single module.
Let's take a look at an example of the SRP. It is written in C++, although you will get the idea even if you do not use that particular programming language. We have a fishing rod service which is responsible for casting and retrieving. There is requirement to prepare fishing rod before fishing, this includes assembling the rod, tying fishing rig and hooking bait.
class FishingRod {
public:
void assembleRod(void) {
printf("Assembling TWO PEACE rod by JOINING TWO PIECES\n");
printf("Placing reel in reel seat and fasten it\n");
printf("Passing line through blank rings\n");
}
void joinRig(void) {
printf("Tying FREE RUNNING rig\n");
}
void prepareHook(void) {
printf("Pulling WORM down onto the hook\n");
}
void cast(void) {
printf("Casting\n");
}
void retrieve(void) {
printf("Retrieving\n");
}
};
This class violates the SRP because functions are responsible to different actors:
The
assembleRod()
andjoinRig()
functions are specified by Fisherman, depending on which blanks and reels are brought to the session.The
prepareHook()
functions is specified and used by Fish, it depends on mood what they want to eat at that time.The
cast()
andretrieve()
functions are specified by Rod'n'Reel combination.
As you can see in this example, this class has more than one responsibility and more than one reason to change. By putting the source code for all functions into single FishingRod
class, developer have coupled all of the actors. If we wanted to change rig, provide new bait for the Fish, both would require changes to the same module.
To solve this issue developer needs to move the functions to different classes.
class AssembleRod {
public:
void blank(void) {
printf("Assembling TWO PEACE rod by JOINING TWO PIECES\n");
printf("Placing reel in reel seat and fasten it\n");
printf("Passing line through 10 blank RINGS\n");
}
void rig(void) {
printf("Tying FREE RUNNING rig\n");
}
};
class PrepareHook {
public:
void worm(void) {
printf("Pulling WORM down onto the hook\n");
}
};
class RodAction {
public:
void cast(void) {
printf("Casting\n");
}
void retrieve(void) {
printf("Retrieving\n");
}
};
class FishingRodFacade: public AssembleRod, public PrepareHook, public RodAction {
public:
void assembleRod(void) {
blank();
}
void joinRig(void) {
rig();
}
void prepareHook(void) {
worm();
}
void castRod(void) {
cast();
}
void retrieveRod(void) {
retrieve();
}
};
To solve this issue developer can use the Facade pattern. The FishingRodFacade
contains very little code. It is responsible for providing simple interface to the client.
All classes are now only responsible for a single thing and therefore has only one reason to change. This implementation no longer violates the Single Rod Principle.
Subscribe to my newsletter
Read articles from Antanas Stankevičius directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by