Software systems often become quite large and complex. In order to understand and manage these
large systems it is important for each part to focus on implementing a single responsibility.
This is important for components at all levels, e.g., functions, classes, packages, modules, and programs.
Using the Single Responsibility Principle:
You describe the component's responsibility in one or two sentences.
The name of each component describes its responsibility.
All data that flows into and out of a component directly supports its responsibility
So how do we build complex systems out of components that each have a single responsibility?
We'll demonstrate with an example. The class diagram below represents a parser used for code analysis in
course projects in Software Modeling & Analysis and Object Oriented Design.
The parser is composed of four modules: Executive, Display, Parser, and Scanner. Their responsibilities are:
Executive:
Build parsing objects and start the parser.
Display:
Display results, derived from Repository data, to the user.
Parser:
Analyze a token stream extracted from the scanner for grammatical constructs in the code it represents.
Scanner:
Extract sequences of tokens from an attached input file.
Each module contains a small number of classes, each with a single responsibility.
The Scanner contains classes:
Toker:
Extract tokens (single words) from an input stream attached to a source code file.
SemiExp:
Extract token sequences from Toker that contain just enough information to detect a grammatical construct.
The Parser contains classes:
Parser:
Extract token sequences from SemiExp and direct them to a set of grammatical construct detection rules.
IRule:
Define the communication protocol for Rules.
Rules:
Each rule detects a specific grammatical construct from a token sequence, e.g., is this a class, a function, ...
IAction:
Define the communication protocol for actions.
Actions:
Implement processing appropriate for the detected construct after its Rule finds a match in the token stream,
e.g., save the name of a discovered function in the repository along with its line number in the source file.
Repository:
Save information provided by actions for use by other actions and the Display instance.
Conclusions for Single Responsibility Principle:
In the parser example we've seen that:
Each of the modules has a single responsibility.
Each of the classes in each module has a single responsibility
These responsibilities are each described in a sentence or two and are relatively easy to understand.
The consequence of using SRP in the parser design is that a quite complex processing implementation has
been broken down into simple to understand parts.
The result is that the amount of time to understand parser well enough to use it is surprisingly short
and the code is much easier to maintain that it would otherwise have been.
The Single Responsiblity Principle is the most important of all of the design principles we'll discuss
in Software Modeling & Analysis and in Object Oriented Design.