Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

The Single Responsibility Principle states that a class should do one and only one thing. Some cases are pretty clear cut. Others, though, are difficult because what looks like "one thing" when viewed at a given level of abstraction may be multiple things when viewed at a lower level. I also fear that if the Single Responsibility Principle is honored at the lower levels, excessively decoupled, verbose ravioli code, where more lines are spent creating tiny classes for everything and plumbing information around than actually solving the problem at hand, can result.

How would you describe what "one thing" means? What are some concrete signs that a class really does more than "one thing"?

share|improve this question
1  
+1 for over the top "ravioli code". Early on in my career I was one of those people who took it too far. Not just with classes, but with method modularization too. My code was peppered with tons of little methods that did something simple, just for the sake of breaking up a problem into small chunks that could fit on the screen without scrolling. Obviously, this was often going way too far. –  Bobby Tables Dec 29 '10 at 20:44

10 Answers 10

up vote 39 down vote accepted

I really like the way Robert C. Martin (Uncle Bob) restates the Single Responsibility Principle (linked to PDF):

There should never be more than one reason for a class to change

It's subtly different from the traditional "should do only one thing" definition, and I like this because it forces you to change the way you think about your class. Instead of thinking about "is this doing one thing?", you instead think about what can change and how those changes affect your class. So for example, if the database changes does your class need to change? What about if the output device changes (for example a screen, or a mobile device, or a printer)? If your class needs to change because of changes from many other directions, then that's a sign that your class has too many responsibilities.

In the linked article, Uncle Bob concludes:

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.

share|improve this answer
1  
I like the way he states it too, it seems easier to check when related to change rather than the abstract "responsibility". –  Matthieu M. Nov 5 '10 at 18:33
    
That's actually an excellent way to put it. I love that. As a note, I usually tend to think of the SRP as applying much more strongly to methods. Sometimes a class just has to do two things (maybe its the class the bridges two domains), but a method should almost never be doing more than what you can succinctly describe by its type signature. –  CodexArcanum Nov 5 '10 at 19:56
1  
Just showed this to my Grad - great read and a damn good reminder to myself. –  Martijn Verburg Nov 8 '10 at 13:39
1  
Ok, this makes a ton of sense when you combine it with the idea that non-overengineered code should only plan for change that's likely in the foreseeable future, not for every possible change. I'd restate this, then, slightly as "there should only be one reason for a class to change that is likely to occur in the foreseeable future". This encourages favoring simplicity in parts of the design that are unlikely to change and decoupling in parts that are likely to change. –  dsimcha Jan 1 '11 at 0:45

I don't know if there is an objective scale to it but what would give this away would be the methods - not so much the number of them but the variety of their function. I agree you can take decomposition too far but I wouldn't follow any hard and fast rules about it.

share|improve this answer

I always view it on two levels:

  • I make sure my methods only do one thing and do it well
  • I see a class as a logical (OO) grouping of those methods that represents one thing well

So something like a domain object called Dog:

Dog is my class but Dogs can do many things! I might have methods such as walk(), run() and bite(DotNetDeveloper spawnOfBill) (sorry couldn't resist ;p).

If Dog becomes to unwieldy then I'd think about how groups of those methods can be modeled together in another class, such as a Movement class, which could contain my walk() and run() methods.

There's no hard and fast rule, your OO design will evolve over time. I try to go for a clear cut interface/public API as well as simple methods that do one thing and one thing well.

share|improve this answer
    
Bite should really take an instance of Object, and a DotNetDeveloper should be a subclass of Person (usually, anyway!) –  Alan Pearce Nov 5 '10 at 15:00
    
@Alan - There - fixed that for you :-) –  Martijn Verburg Nov 5 '10 at 15:08

It's about having one unique rôle.

Each class should be resumed by a role name. A role is in fact a (set of) verb(s) associated with a context.

For example :

File provide a file's access. FileManager manage File objects.

Resource hold data for one resource from a File. ResourceManager hold and provide all Resources.

Here you can see that some verbs like "manage" imply a set of other verbs. Verbs alone are better thought as functions than classes, most of the time. If the verb imply too much actions that have their own common context, then it should be a class in itself.

So, the idea is only to let you have a simple idea of what does the class by defining a unique role, that might be the agregate of several sub-role (performed by member objects or other objects).

I often build Manager classes that have several different other classes in it. Like a Factory, a Registry, etc. See a Manager class like some kind of group chief, a orchestra chief that guide other peoples to work together to achieve a high level idea. He have one role, but imply working with other unique roles inside. You can also see it like how a company is organized : a CEO isn't a productive one on the pure productivity level, but if he is not there, then nothing can work correctly together. That's his role.

When you design, identify unique roles. And for each role, again see if it can't be cut in several other roles. That way, if you need to simlpy change the way your Manager build objects, simply change the Factory and go with peace in mind.

share|improve this answer

I look at it more along the lines of a class should only represent one thing. To appropriate @Karianna's example, I have my Dog class, which has methods for walk(), run() and bark(). I'm not going to add methods for meaow(), squeak(), slither() or fly() because those aren't things that dogs do. They are things that other animals do, and those other animals would have their own classes to represent them.

(BTW, if your dog does fly, then you should probably stop throwing him out of the window).

share|improve this answer
    
+1 for "if your dog does fly, then you should probably stop throwing him out of the window". :) –  Bobby Tables Dec 29 '10 at 20:39
    
Beyond the question of what the class should represent, what does an instance represent? If one regards SeesFood as a characteristic of DogEyes, Bark as something done by a DogVoice, and Eat as something done by a DogMouth, then logic like if (dog.SeesFood) dog.Eat(); else dog.Bark(); will become if (eyes.SeesFood) mouth.Eat(); else voice.Bark();, losing any sense of identity that eyes, mouth, and voice are all connected to a single entitity. –  supercat Mar 25 at 19:20
    
@supercat it's a fair point, though context is important. If the code you mention is within the Dog class, then it's probably Dog-related. If not, then you would probably end up with something like myDog.Eyes.SeesFood rather than just eyes.SeesFood. Another possibility is that Dog exposes the ISee interface which demands the Dog.Eyes property and the SeesFood method. –  JohnL Apr 1 at 13:32
    
@JohnL: If the actual mechanics of seeing are handled by a dog's eyes, in essentially the same way as by a cat's or a zebra's, then it may make sense to have the mechanics handled by an Eye class, but a dog should "see" using its eyes, rather than merely having eyes that can see. A dog isn't an eye, but nor is it simply an eye-holder. It is a "thing that can [at least try to] see", and should be described via interface as being such. Even a blind dog can be asked if it sees food; it won't be very useful, since the dog will always say "no", but there's no harm in asking. –  supercat Apr 1 at 18:04
    
Then you'd use the ISee interface like I describe in my comment. –  JohnL Apr 2 at 9:16

I keep asking myself what problem is SRP trying to solve? When does SRP help me? Here’s what I came up with:

You should refactor responsibility / functionality out of a class when:

1) You’ve duplicated functionality (DRY)

2) You find that your code needs another level of abstraction in order to help you understand it (KISS)

3) You find that pieces of functionality are understood by your domain experts as being apart of a different component (Ubiquitous Language)

You SHOULDN’T refactor responsibility out of a class when:

1) There isn’t any duplicated functionality.

2) The functionality doesn’t make sense outside of the context of your class. Put another way, your class provides a context in which it is easier to understand the functionality.

3) Your domain experts don’t have a concept of that responsibility.

It occurs to me that if SRP is applied to broadly, we trade one kind of complexity (trying to make heads or tails of a class with way too much going on inside) with another kind (trying to keep all of the collaborators / levels of abstraction straight in order to figure out what these classes actually do).

When in doubt, leave it out! You can always refactor later, when there is a clear case for it.

What do you think?

share|improve this answer

A class should do one thing when viewed at its own level of abstraction. It will doubtless do many things at a less abstract level. This is how classes work to make programs more maintainable: they hide implementation details if you don't need to examine them closely.

I use class names as a test for this. If I can't give a class a fairly short descriptive name, or if that name has a word like "And" in it, I'm probably violating the Single Responsibility Principle.

In my experience, it's easier to keep this principle at the lower levels, where things are more concrete.

share|improve this answer

The Answer lies in the definition

What you define the responsibility to be, ultimately gives you the boundary.

Example:

I have a component that has the responsibility of displaying invoices -> in this case, if I started to add anything more then I'm breaking the principle.

If on the other hand if I said responsibility of handling invoices -> adding multiple smaller functions (eg printing Invoice, updating Invoice) all are within that boundary.

However if that module started to handle any functionality outside of invoices, then it would be outside of that boundary.

share|improve this answer
    
I guess the main problem here is the word "handling". It's too generic to define a single responsibility. I think it would be better to have a component responsible for print invoice and another one for update invoice rather than a single component handle{print, update, and - why not ? - display, whatever} invoice. –  Machado Dec 30 '10 at 3:27
    
The OP is essentially asking "How do you define a responsibility?" so when you say the responsibility is whatever you define it to be seems like it's just repeating the question. –  Despertar Jul 17 '13 at 22:31

The SRP is not just about dividing classes up, but also about delegating functionality.

In the Dog example used above, don't use SRP as justification to have 3 separate classes such as DogBarker, DogWalker, etc (low cohesion). Instead, look at the implementation of a class's methods and decide if they "know too much". You can still have dog.walk(), but probably the walk() method should delegate to another class the details of how walking is accomplished.

In effect we are allowing the Dog class to have one reason to change: because Dogs change. Of course, combining this with other SOLID principles you would Extend Dog for new functionality rather than changing Dog (open/closed). And you would Inject your dependencies such as IMove and IEat. And of course you would make these separate interfaces (interface segregation). Dog would only change if we found a bug or if Dogs fundamentally changed (Liskov Sub, don't extend and then remove behavior).

The net effect of SOLID is that we get to write new code more often than we have to modify existing code, and that is a big win.

share|improve this answer

It all depends on the definition of responsibility and how that definition is going to impact maintenance of your code. Everything boils down to one thing and that is how your design is going to help you in maintaining your code.

And Like someone said "Walking on water and designing software from given specs is easy, provided both are frozen" .

So, if we are defining responsibility in more concrete way, then we don't need to change it.

Sometimes responsibilities will be glaringly obvious, but sometimes it might be subtle and we need to judiciously decide.

Suppose, we add another responsibility to Dog class named catchThief(). Now it might be leading towards an additional different responsibility. Tomorrow, if the way Dog is catching thief has to be changed by Police Dept, then Dog class will have to be changed. It would be better to create another subclass in this case and name it ThiefCathcerDog. But in a different view, if we are sure that it is not going to change in any circumstance, or the way catchThief has been implemented is dependent on some external parameter, then it is perfectly ok to have this responsibility. If responsibilities are not strikingly odd then we have to decide it judiciously based on the use case.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.