Previous post in this series:
SOLID – Getting Started and Source Material
I’m sure most of us have taken an Object Oriented Programming (OOP) class in college (or will soon). What is important to understand is that an OOP class will teach you the fundamentals of understanding the principles of developing from an object-centric perspective. Uncle Bob’s SOLID principles takes those concepts from OOP and develops them further into object oriented design (OOD), how each object should be developed so that long term maintenance of software is easier, cleaner and ultimately cheaper.
The SRP is one of the simplest of the principles, and one of the hardest to get right. Conjoining responsibilities is something that we do naturally. Finding and separating those responsibilities from one another is much of what software design is really about. Indeed, the rest of the principles we will discuss come back to this issue in one way or another.
Agile Principles, Patterns, and Practices in C#, Robert C. Martin
SRP is pretty easy right? An object should only have one responsibility. As an example lets look at a Vehicle, the age old example of OOP classes everywhere. What is the responsibility of a vehicle? It is used to transport people and/or things.
What do we need to know about the vehicle so it can accomplish it’s responsibility?
- How many people or things it can hold?
- Is it capable of transporting those things?
- Does it have at least one entry to allow for putting things in and out?
- Does it have the ability to move (i.e. tires or sleds)?
- Does it have a working means of propulsion?
What don’t we care about?
- How big is the engine?
- How does the engine work?
- What does it look like?
- How many tires does it have (if any)?
- What kind of tires are they?
As Uncle Bob mentions, we naturally want to conjoin responsibilities. We want to put everything in the vehicle and call it a day. But a vehicle shouldn’t define what an engine is, how it works and what it does. It may define how it uses that engine but the engine it’s self shouldn’t be defined within the vehicle.
That’s an important part to the SRP. It doesn’t say an object can’t contain other objects that facilitate it’s responsibility. An engine isn’t a vehicle and a vehicle isn’t an engine. A vehicle shouldn’t define what an engine is within it’s self. There is nothing wrong, however, with it have a Engine property.
To try and make this a bit clearer:
The Single Responsibility Principle (SRP) says that a class should have one, and only one, reason to change. To say this a different way, the methods of a class should change for the same reasons, they should not be affected by different forces that change at different rates
SRP in Ruby, Robert C. Martin
This (slightly modified) code sample from Microsoft on rectangles gives a good example of this:
<Window x:Class="Wpf_Test.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow"> <Canvas> <Path Stroke="Black" StrokeThickness="1" Fill="LemonChiffon"> <Path.Data> <RectangleGeometry> <RectangleGeometry.Rect> <Rect X="10" Y="100" Width="150" Height="100" /> </RectangleGeometry.Rect> </RectangleGeometry> </Path.Data> </Path> </Canvas> </Window>
All this does is draw a rectangle with the class RectangleGeometry. What is important here is that there is a property, Rect, that defines the the properties of where a rectangle is and how big it is. The struct, Rect, has a bunch of operations that have nothing to do with drawing a rectangle on the canvas, methods like IntersectsWith(Rect) and Union(Rect). Whereas RectangleGeometry has Freeze() and GetRenderBounds() that have to do with drawing the rectangle on the screen but have nothing to do with directly working with rectangles. Thus a RectangleGeometry has a Rect property to take care of these things.
So know we have the instructions on the paint can. But when do not listen to them? That’s up in the next post.