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. Important characteristics of the SRP are:
- You can describe the component's responsibility in one or two sentences.
- The name of each component describes its responsibility.
- All the 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 that by example. The class diagram below represents a parser used for code analysis.
The parser is composed of four modules: Executive, Display, Parser, and Scanner. Their responsibilities are:
-
Executive:
Build and start the parser. -
Display:
Display results, derived from Repository data, to the user. -
Parser:
Analyze a token stream extracted from the scanner and analyze 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 provided by a compiler class library. -
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 to a set of grammatic 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, function, ... -
IAction:
Define the communication protocol for actions. -
Actions:
Implement processing necessary to take a specific action 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 the Display class.
Conclusions for Single Responsibility Principle:
- 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 our classes.