80

The Single responsibility principle is defined on wikipedia as

The single responsibility principle is a computer programming principle that states that every module, class, or function should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class

If a class should only have a single responsibility, how can it have more than 1 method? Wouldn't each method have a different responsibility, which would then mean that the class would have more than 1 responsibility.

Every example I've seen demonstrating the single responsibility principle uses an example class that only has one method. It might help to see an example or to have an explanation of a class with multiple methods that can still be considered to have one responsibility.

Goose
  • 1,858
  • 2
  • 15
  • 27
  • 20
    Why a downvote? Seems like an ideal question for SE.SE; the person researched the topic, and made effort making the question very clear. It deserves upvotes instead. – Arseni Mourzenko Jul 31 '19 at 20:04
  • 20
    The downvote was probably due to this being a question that has been asked and answered several times already, for example see https://softwareengineering.stackexchange.com/questions/345018/when-using-the-single-responsibility-principle-what-constitutes-a-responsibili. In my opinion, it doesn't add substantial new aspects. – Hans-Martin Mosner Aug 01 '19 at 05:16
  • 13
    Possible duplicate of [When using the Single Responsibility Principle, what constitutes a "responsibility?"](https://softwareengineering.stackexchange.com/questions/345018/when-using-the-single-responsibility-principle-what-constitutes-a-responsibili) – Bart van Ingen Schenau Aug 01 '19 at 07:15
  • 1
    Possible duplicate of [Do setters and getters always break the Single-Responsibility Principle?](https://softwareengineering.stackexchange.com/questions/195961/do-setters-and-getters-always-break-the-single-responsibility-principle) – Blrfl Aug 01 '19 at 11:46
  • 12
    This is simply reductio ad absurdum. If *every* class literally were allowed only one method, then there's literally no way any program would ever be able to do more than one thing. – Darrel Hoffman Aug 01 '19 at 14:24
  • 1
    This question might provide some insight: ["How do you determine how coarse or fine-grained a 'responsibility' should be when using the single responsibility principle?"](https://stackoverflow.com/q/2455705/25847) – Mark Rogers Aug 01 '19 at 14:51
  • 1
    Here are some [highly related anecdotes](https://softwareengineering.stackexchange.com/questions/14856/what-popular-best-practices-are-not-always-best-and-why/38504#38504) – BlueRaja - Danny Pflughoeft Aug 01 '19 at 19:00
  • 6
    @DarrelHoffman That's not true. If every class were a functor with only a "call()" method, then you've basically just emulated plain procedural programming with object oriented programming. You can still do anything you could have done otherwise, since a class'es "call()" method can call many other classes' "call()" methods. – Vaelus Aug 02 '19 at 03:14
  • 2
    The SRP is confusing - as comes up all the time here - and overrated. I much prefer Parnas' prescription which is much easier to understand and is also actionable: "Begin with a list of difficult design decisions or design decisions which are likely to change. Each module is then designed to hide such a decision from the others." See _On the Criteria To Be Used in Decomposing Systems Into Modules_, David Parnas, CACM Dec 1972. – davidbak Aug 02 '19 at 17:10
  • It may be relevant to point out that most responsibilities tend to involve other, subservient responsibilities. For example, a `CoffeeIntern`'s responsibility is the preparation of coffee, which likely involves methods `boilWater()` and `stir()`, each of which has its own responsibility (and where each responsibility is a component of the overarching "prepare coffee" responsibility). – Justin Time - Reinstate Monica Aug 03 '19 at 01:14
  • Although an SRP-compliant class may have multiple methods, I find that in practice I write more and more one-method classes for pretty much the reasons you've described. If it only has one responsibility I'm likely to only do one thing with it. The exception might be library-type classes that provide reusable functionality. So many classes end up being containers to encapsulate the state and allow me to break down that function into smaller ones. On a separate note, a domain object can have a single responsibility - representing a domain concept, but numerous methods describing interactions. – Scott Hannen Oct 04 '19 at 18:48

8 Answers8

56

The key here is scope, or, if you prefer, granularity. A part of functionality represented by a class can be further separated into parts of functionality, each part being a method.

Here's an example. Imagine you need to create a CSV from a sequence. If you want to be compliant with RFC 4180, it would take quite some time to implement the algorithm and handle all the edge cases.

Doing it in a single method would result in a code which won't be particularly readable, and especially, the method would do several things at once. Therefore, you will split it into several methods; for instance, one of them may be in charge of generating the header, i.e. the very first line of the CSV, while another method would convert a value of any type to its string representation suited for CSV format, while another one would determine if a value needs to be enclosed into double quotes.

Those methods have their own responsibility. The method which checks whether there is a need to add double quotes or not has its own, and the method which generates the header has one. This is SRP applied to methods.

Now, all those methods have one goal in common, that is, take a sequence, and generate the CSV. This is the single responsibility of the class.


Pablo H commented:

Nice example, but I feel it still doesn't answer why SRP allows a class to have more than one public method.

Indeed. The CSV example I gave has ideally one public method and all other methods are private. A better example would be of a queue, implemented by a Queue class. This class would contain, basically, two methods: push (also called enqueue), and pop (also called dequeue).

  • The responsibility of Queue.push is to add an object to the queue's tail.

  • The responsibility of Queue.pop is to remove an object from the queue's head, and handle the case where the queue is empty.

  • The responsibility of Queue class is to provide a queue logic.

Arseni Mourzenko
  • 134,780
  • 31
  • 343
  • 513
35

The single responsibility might not be something that a single function can fulfill.

 class Location { 
     public int getX() { 
         return x;
     } 
     public int getY() { 
         return y; 
     } 
 }

This class may break the single responsibility principle. Not because it has two functions, but if the code for getX() and getY() have to satisfy different stakeholders that may demand change. If Vice President Mr. X sends around a memo that all numbers shall be expressed as floating point numbers and Accounting Director Mrs. Y insists that all numbers her department reviews shall remain integers regardless of what Mr. X thinks well then this class had better have a single idea of who it's responsible to because things are about to get confusing.

If SRP had been followed it would be clear if the Location class contributes to things Mr X and his group are exposed to. Make clear what the class is responsible to and you know which directive impacts this class. If they both impact this class then it was poorly designed to minimize the impact of change. "A class should only have one reason to change" doesn't mean the entire class can only ever do one little thing. It means I shouldn't be able to look at the class and say that both Mr X and Mrs Y have an interest in this class.

Other than things like that. No, multiple methods are fine. Just give it a name that makes clear what methods belong in the class and what ones don't.

Uncle Bob's SRP is more about Conway's Law than Curly's Law. Uncle Bob advocates applying Curly's Law (do one thing) to functions not classes. SRP cautions against mixing reasons to change together. Conway's Law says the system will follow how an organization's information flows. That leads to following SRP because you don't care about what you never hear about.

"A module should be responsible to one, and only one, actor"

Robert C Martin - Clean Architecture

People keep wanting SRP to be about every reason to limit scope. There are more reasons to limit scope than SRP. I further limit scope by insisting the class be an abstraction that can take a name that ensures looking inside won't surprise you.

You can apply Curly's Law to classes. You're outside what Uncle Bob talks about but you can do it. Where you go wrong is when you start to think that means one function. That's like thinking a family should only have one child. Having more than one child doesn't stop it from being a family.

If you apply Curly's law to a class, everything in the class should be about a single unifying idea. That idea can be broad. The idea might be persistence. If some logging utility functions are in there, then they are clearly out of place. Doesn't matter if Mr X is the only one who cares about this code.

The classic principle to apply here is called Separation of Concerns. If you separate all your concerns it could be argued that what's left in any one place is one concern. That's what we called this idea before the 1991 movie City Slickers introduced us to the character Curly.

This is fine. It's just that what Uncle Bob calls a responsibility isn't a concern. A responsibility to him isn't something you focus on. It's something that can force you to change. You can focus on one concern and still create code that is responsible to different groups of people with different agendas.

Maybe you don't care about that. Fine. Thinking that holding to "do one thing" will solve all of your design woes shows a lack of imagination of what "one thing" can end up being. Another reason to limit scope is organization. You can nest many "one thing"s inside other "one thing"s until you have a junk drawer full of everything. I've talked about that before

Of course the classic OOP reason to limit scope is that the class has private fields in it and rather then use getters to share that data around, we put every method that needs that data in the class where they can use the data in private. Many find this too restrictive to use as a scope limiter because not every method that belongs together uses exactly the same fields. I like to ensure that whatever idea that brought the data together be the same idea that brought the methods together.

The functional way to look at this is that a.f(x) and a.g(x) are simply fa(x) and ga(x). Not two functions but a continuum of pairs of functions that vary together. The a doesn't even have to have data in it. It could simply be how you know which f and g implementation you're going to use. Functions that change together belong together. That's good old polymorphism.

SRP is just one of many reasons to limit scope. It's a good one. But not the only one.

candied_orange
  • 102,279
  • 24
  • 197
  • 315
  • 29
    I think this answer is confusing to someone trying to figure out the SRP. The battle between Mr. President and Mrs Director is not solved through technical means and using it to justify an engineering decision is nonsensical. Conway's law in action. – whatsisname Aug 01 '19 at 04:28
  • 10
    @whatsisname On the contrary. The SRP was explicitly meant to apply to stakeholders. It has *nothing* to do with technical design. You may disagree with that approach, but that's how SRP has been originally defined by Uncle Bob, and he had to reiterate it over and over because for some reason, people don't seem to be able to understand this simple notion (mind, whether it's actually useful is a completely orthogonal question). – Luaan Aug 01 '19 at 06:50
  • Curly's Law, as [described](https://bit.ly/2YJE6Ys) by Tim Ottinger, emphasizes that a variable should consistently *mean* one thing. To me, SRP is a bit stronger than that; a class can conceptually represent "one thing", yet violate SRP if two external drivers of change treat some aspect of that "one thing" in different ways, or are concerned about two different aspects. The problem is one of modeling; you've chosen to model something as a single class, but there's something about the domain that makes that choice problematic (things start getting in your way as the codebase evolves). – Filip Milovanović Aug 01 '19 at 19:01
  • The term "responsibility" expresses that the class is trying to serve more then one need (in the above sense) - it has more then one well-defined axis of change. The problem is that under those circumstances, the code for the two tends to get more and more coupled if left unchecked, which ends up making those (and, even worse, seemingly unrelated) changes harder. – Filip Milovanović Aug 01 '19 at 19:01
  • P.S. I disagree with the point about Conway's Law, though: Conway's Law is an observation about what typically tends to happen in software development world, not something to strive for (or avoid). It's about the communication structures of the *dev* organisation; SRP, on the other hand, primarily reflects the way *domain experts* go about their work. – Filip Milovanović Aug 01 '19 at 19:01
  • 2
    @FilipMilovanović The similarity I see between Conway's Law and SRP they way Uncle Bob explained SRP in his Clean Architecture book comes from the assumption that the organization has a clean acyclical org chart. This is an old idea. Even the Bible has a quote here: "No man can serve two masters". – candied_orange Aug 01 '19 at 19:12
  • Your reference for Curly's Law contradicts you by equating it to the SRP. – StackOverthrow Aug 01 '19 at 20:50
  • 1
    @TKK im relating it (not equating it) to Conways's law not Curly's law. I'm refuting the idea that SRP is Curly's law mostly because Uncle Bob said so himself in his Clean Architecture book. – candied_orange Aug 01 '19 at 21:29
  • @candied_orange: I understand (and I see the similarity), I'm just pointing out that Conway's Law is reflected in things like "we have two dev teams - so we'll divide the system into two components", while SRP is more about "they keep asking us for new features for this bit, so let's separate that out", or even, on a larger scale "the business is treating this concept in two ways - let's make two bounded contexts". Now, there is some overlap here, because the two orgs (dev and business) need to collaborate, so I guess that some generalization of Conway's Law applies as well. – Filip Milovanović Aug 02 '19 at 05:29
  • @candied_orange That's my point. You're refuting the idea that the SRP is equivalent to Curly's Law, but you've chosen a reference that equates them. – StackOverthrow Aug 02 '19 at 16:12
  • @candied_orange This answer really makes Uncle Bob's ideas make a lot more sense to me. Thanks! – jpaugh Aug 02 '19 at 19:48
  • @TKK I know it's confusing but it's exactly what's happening. Uncle Bob thinks his SRP is just about limiting reasons to change by giving authoritative stakeholders each their own modules to preside over. However, plenty of people want SRP to be about more than that. Problem is, SRP is Uncle Bobs idea and he's made his opinion clear. So the SRP has its own SRP problem. We need our own principle for this. I chose "Least Astonishment" – candied_orange Aug 03 '19 at 11:21
  • @candied_orange I understand both viewpoints, and I'm not defending one or the other. I'm simply pointing out that the reference you chose to link contradicts your viewpoint. – StackOverthrow Aug 05 '19 at 16:44
34

A function is a function.

A responsibility is a responsibility.

A mechanic has the responsibility to fix cars, which will involve diagnostics, some simple maintenance tasks, some actual repair work, some delegation of tasks to others, etc. Things will generally be simpler and more efficient if you don't ask that same mechanic to do advertising and billing.

A container class (list, array, dictionary, map, etc) has the responsibility to store objects, which involves storing them, allowing insertion, providing access, some kind of ordering, etc.

A single responsibility doesn't mean there is very little code/functionality, it means whatever functionality there is "belongs together" under the same responsibility.

Peter
  • 3,718
  • 1
  • 12
  • 20
  • 3
    Concur. @Aulis Ronkainen - to tie in the two answers. And for nested responsibilities, using your mechanic analogy, a garage has the responsibility for the maintenance of vehicles. different mechanics in the garage have the responsibility for different parts of the car, but each of these mechanics works together in cohesion – wolfsshield Aug 01 '19 at 17:42
  • 2
    @wolfsshield, agreed. Mechanic that only does one thing is useless, but mechanic that has a single responsibility isn't (at least necessarily). Although real life analogies aren't always the best to describe abstract OOP concepts, it is important to distinguish these differences. I believe not understanding the difference is what creates the confusion in the first place. – Aulis Ronkainen Aug 01 '19 at 19:27
21

Single responsibility does not necessarily mean it only does one thing.

Take for example a User service class:

class UserService {
    public User Get(int id) { /* ... */ }
    public User[] List() { /* ... */ }

    public bool Create(User u) { /* ... */ }
    public bool Exists(int id) { /* ... */ }
    public bool Update(User u) { /* ... */ }
}

This class has multiple methods but it's responsibility is clear. It provides access to the user records in the data store. Its only dependencies are the User model and the data store. It's loosely coupled and highly cohesive, which really is what SRP is trying to get you to think about.

SRP should not be confused with the "Interface segregation principle" (see SOLID). The Interface segregation principle (ISP) says that smaller, lightweight interfaces are preferable to larger more generalized interfaces. Go makes heavy use of ISP throughout its standard library:

// Interface to read bytes from a stream
type Reader interface {
    Read(p []byte) (n int, err error)
}

// Interface to write bytes to a stream
type Writer interface {
    Write(p []byte) (n int, err error)
}

// Interface to convert an object into JSON
type Marshaler interface {
    MarshalJSON() ([]byte, error)
}

SRP and ISP are certainly related, but one does not imply the other. ISP is at the interface level and SRP is at the class level. If a class implements several simple interfaces, it may no longer have just one responsibility.

Thanks to Luaan for pointing out the difference between ISP and SRP.

Jessie
  • 512
  • 2
  • 9
  • 3
    Actually, you're describing the interface segregation principle (the "I" in SOLID). SRP is quite a different beast. – Luaan Aug 01 '19 at 06:52
  • As an aside, which coding convention are you using here? I would expect the _objects_ `UserService` and `User` to be UpperCamelCase, but the _methods_ `Create`, `Exists`, and `Update` I would have made lowerCamelCase. – KlaymenDK Aug 01 '19 at 13:31
  • 1
    @KlaymenDK You're right, the uppercase is just a habit from using Go (uppercase = exported/public, lowercase = private) – Jessie Aug 01 '19 at 16:00
  • @Luaan Thanks for pointing that out, I'll clarify my answer – Jessie Aug 01 '19 at 16:02
  • 1
    @KlaymenDK Lots of languages use PascalCase for methods as well as classes. C# for example. – Omegastick Aug 02 '19 at 10:00
  • @Omegastick Yeah, I was just curious (and I haven't written Pascal in, huh, decades so it wasn't on the top of my mind). – KlaymenDK Aug 02 '19 at 11:58
  • The difference between SRP and ISP isn't class and interface. Classes have interfaces. ISP is from the perspective of the class that depends on a class/interface, not that class itself. If it uses all of the members it doesn't violate ISP. If it uses some, it violates ISP. – Scott Hannen Oct 04 '19 at 18:44
18

There is a chef in a restaurant. His only responsibility is to cook. Yet he can cook steaks, potatoes, broccoli, and hundred other things. Would you hire one chef per dish on your menu? Or one chef for each component of each dish? Or one chef who can meet his single responsibility: To cook?

If you ask that chef to do the payroll as well, that’s when you violate SRP.

gnasher729
  • 42,090
  • 4
  • 59
  • 119
7

You're misinterpreting the single responsibility principle.

Single responsibility doesn't equal a single method. They mean different things. In software development we talk about cohesion. Functions (methods) that have high cohesion "belong" together and can be counted as performing a single responsibility.

It's up to the developer to design the system so that the single responsibility principle is fulfilled. One can see this as an abstraction technique and is therefore sometimes a matter of opinion. Implementing the single responsibility principle makes the code mainly easier to test and easier to understand its architecture and design.

Peter Mortensen
  • 1,050
  • 2
  • 12
  • 14
Aulis Ronkainen
  • 187
  • 1
  • 2
  • 10
5

Counter-example: storing mutable state.

Suppose you had the simplest class ever, whose only job is to store an int.

public class State {
    private int i;


    public State(int i) { this.i = i; }
}

If you were limited to only 1 method, you could either have a setState(), or a getState(), unless you break encapsulation and make i public.

  • A setter is useless without a getter (you could never read the information)
  • A getter is useless without a setter (you can never mutate the information).

So clearly, this single responsibility necessitates having at least 2 methods on this class. QED.

hellyale
  • 103
  • 3
Alexander
  • 3,562
  • 1
  • 19
  • 24
3

It's often helpful (in any language, but especially in OO languages) to look at things and organize them from the viewpoint of the data rather than the functions.

Thus, consider the responsibility of a class to be to maintain the integrity of and provide help to correctly use the data it owns. Clearly this is easier to do if all the code is in one class, rather than spread out over several classes. Adding two points is more reliably done, and the code more easily maintained, with a Point add(Point p) method in the Point class than having that elsewhere.

And in particular, the class should expose nothing that could result in inconsistent or incorrect data. For example, if a Point must lie within a (0,0) through (127,127) plane, the constructor and any methods that modify or produce a new Point have the responsibility of checking the values they're given and rejecting any changes that would violate this requirement. (Often something like a Point would be immutable, and ensuring that there are no ways of modifying a Point after it's constructed would then also be a responsbility of the class)

Note that layering here is perfectly acceptable. You might have a Point class for dealing with individual points and a Polygon class for dealing with a set of Points; these still have separate responsibilities because Polygon delegates all responsibility for dealing with anything solely to do with a Point (such as ensuring a point has both an x and a y value) to the Point class.

cjs
  • 787
  • 4
  • 8