Agile Principles, Patterns, and Practices in C#

© Jennifer M. Kohnke

None but Buddha himself must take the responsibility of giving out occult secrets . . .

E. Cobham Brewer 18101897, Dictionary of Phrase and Fable (1898)

This principle was described in the work of Tom DeMarco[1] and Meilir Page-Jones.[2] They called it cohesion, which they defined as the functional relatedness of the elements of a module. In this chapter, we modify that meaning a bit and relate cohesion to the forces that cause a module, or a class, to change.

[1] [DeMarco79], p. 310

[2] [PageJones88], p. 82

The Single-Responsibility Principle

A class should have only one reason to change.

Consider the bowling game from Chapter 6. For most of its development, the Game class was handling two separate responsibilities: keeping track of the current frame and calculating the score. In the end, RCM and RSK separated these two responsibilities into two classes. The Game kept the responsibility to keep track of frames, and the Scorer got the responsibility to calculate the score.

Why was it important to separate these two responsibilities into separate classes? The reason is that each responsibility is an axis of change. When the requirements change, that change will be manifest through a change in responsibility among the classes. If a class assumes more than one responsibility, that class will have more than one reason to change.

If a class has more than one responsibility, the responsibilities become coupled. Changes to one responsibility may impair or inhibit the class's ability to meet the others. This kind of coupling leads to fragile designs that break in unexpected ways when changed.

For example, consider the design in Figure 8-1. The Rectangle class has two methods shown. One draws the rectangle on the screen, and the other computes the area of the rectangle.

Figure 8-1. More than one responsibility

Two different applications use the Rectangle class. One application does computational geometry. Using Rectangle to help it with the mathematics of geometric shapes but never drawing the rectangle on the screen. The other application is graphical in nature and may also do some computational geometry, but it definitely draws the rectangle on the screen.

This design violates SRP. The Rectangle class has two responsibilities. The first responsibility is to provide a mathematical model of the geometry of a rectangle. The second responsibility is to render the rectangle on a GUI.

The violation of SRP causes several nasty problems. First, we must include GUI in the computational geometry application. In .NET, the GUI assembly would have to be built and deployed with the computational geometry application.

Second, if a change to the GraphicalApplication causes the Rectangle to change for some reason, that change may force us to rebuild, retest, and redeploy the ComputationalGeometryApplication. If we forget to do this, that application may break in unpredictable ways.

A better design is to separate the two responsibilities into two completely different classes, as shown in Figure 8-2. This design moves the computational portions of Rectangle into the GeometricRectangle class. Now changes made to the way rectangles are rendered cannot affect the ComputationalGeometryApplication.

Figure 8-2. Separated responsibilities

Категории