Agile FAQs
  About   Slides   Home  

 
Managed Chaos
Naresh Jain’s Random Thoughts on Software Development and Adventure Sports
     
`
 
Discovering...
Industrial Logic

Microblog Feed
    Previous Feeds...
    Recent Thoughts

    Recent Comments
    Categories
    Archives
    October 2009
    M T W T F S S
    « Sep   Nov »
     1234
    567891011
    12131415161718
    19202122232425
    262728293031  
    RSS Feed
    Add to Technorati Favorites

    Single Responsibility Principle Demystified

    Lately, I’ve sensed some confusion in developers around the Single Responsibility Principle (SRP). Its easy to say every object or abstraction should have a single responsibility. But it’s important to understand what do we mean by responsibility. (Its like telling someone who really lives to eat, that they should control their diet. Of course they control their diet, just that they have a very different definition of diet.)

    So when we talk about SRP, what does single responsibility really mean?

    Some developers suggest each public method on a object is exposing a responsibility. If we go by this definition of responsibility we’ll end up with every object having exactly one method. Which does not feel right.

    Some developers suggest that each object should do exactly one thing. What do they mean by one thing? When I look at a class or a method, how can I tell if its doing one thing or more than one thing? This definition of SRP is even more vague than the original definition.

    For example, if we have a class Foo, do you think, adding the toString() method violates SRP? Do you think toString() is the responsibility of the Foo class? Most developers would agree that toString() really belongs on the Foo class and it does not violate SRP. toString() method usually needs access to the internal instance variables and hence this method really belongs on the respective class.

    Lets say, we agree that toString() method should belong on the Foo class. Now going by the same argument, can we add toXML() method on the same class? If no, why not? If yes, when will you stop?

    Basically this is a slipper slope. Trying to understand SRP by defining Responsibility does not work really well for me. Instead I look at it from the “Single Reason for Change” lens.  If I look at a class Foo with a method bar() on it; if the class Foo and the method bar() change for 2 different reasons then I think it violates SRP. Irrespective of how many methods it has and how many things it does. (It could be violating other design principles).

    Going back to our example, I think adding a toString() method does not violate the SRP coz I don’t see any reason why only the toString() method needs to change and the rest of the class remains unchanged. If we add or remove instance variables, its most likely that both the class and the toString() method will change.

    Now what about toXML() method? I think the toXML() method does not belong on the object, coz I see 2 reasons for which the toXML() method can change.

    1. When we add/remove any instance variables
    2. When the XML representation/format changes.

    However there is a trade-off here. If you don’t put any method on the class, then how do other objects get the data out (other than breaking encapsulation. Even doing so will increase the coupling.)? On the other hand, if you add such methods, then any changes in the format/representation will effect this object as well. Which does not seem right. So how do you design something which does not violate SRP, does not break encapsulation and also adheres to Open Closed Principle?

    One way to solve this puzzle is to add a to() method which takes a Formatter object, so we end up with a to(Formatter collector) method. Depending on what format (XML, YAML, etc) one needs to pass the appropriate Formatter type. For example if you want the XML representation of this Class, you pass a XMLFormatter object to the to() method. The class which has the to() method is responsible to adding what ever data it needs to expose and the Formatter object is responsible for the representation and formatting. This way if the XML schema changes, there is only one places to go make the change. If the instance variables change there is only one place to make the change. (Again this is not 100% true, but this solution is a step forward from the first solution as far as SRP goes).

    So look for Reasons to Change, as a way to understand SRP.

    BTW, In Uncle Bob’s SRP paper (pdf), he defines SRP as:

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

    Frankly I find this a little too restrictive. I think there can be more than 1 reasons for the class to change. As far as all those reasons affect the whole class (all or most of the methods) the same way, I think we’ve achieved high cohesion and we can be happy for now.

    Also its worth highlighting that SRP applies at multiple levels, right on top at a project level all the way down to your individual lines of code.

    • Share/Bookmark
    • pschwarz
      In Refactoring, Martin Fowler says: Divergent change is one class that suffers many kinds of changes, and shotgun surgery is one change that alters many classes. Either way you want to arrange things so that, ideally, there is a one-to-one link between common changes and classes.

      In my view, the SRP, by getting us to avoid both code smells, is getting us to set up this one-to-one link between common changes and classes.
    • @Philip, that's absolutely correct. That's my thinking as well.
    • pschwarz
      As Uncle Bob said in answer to a question I asked in his Getting a SOLID start: SRP says to keep together things that change for the same reason, and separate things that change for different reasons

      I felt that SRP and Martin Fowler's code smell of Divergent Change were very closely related, so I asked for his opinion, and he replied as follows: Divergent Change occurs when you group together things that change for different reasons. Shotgun Surgery happens when you keep apart those things that change for the same reaon. So, SRP is about both Divergent Change and Shotgun Surgery. Failure to follow SRP leads to both symptoms.
    • pschwarz
      I also view SRP as a cohesion principle. In ASD:PPP, Uncle Bob says of the SRP: This principle was described in the work of Tom DeMarco and Meilir Page-Jones. They called it cohesion...we modify that meaning a bit and relate cohesion to the forces that cause a module, or a class, to change.
    • shahkalpesh
      Naresh,

      I agree with most of the things above.

      Regarding toString, it depends whether it violates SRP. What is the purpose of toString?

      If it is going to be used somewhere, it makes sense. The toString (without any overload) doesn't seem to get used. For cases, where the string representation is needed where caller should have control over the output, the overload with formatter comes into play.

      The same is true for toXML. Because, the method will be used somewhere and it will be meaningful to have it outside the class, so that caller can have XML formatted his/her way.

      So, I think of responsibility = "use case" or aspect which can have different implementation. And hence, it should not be frozen.

      This is how, it combines with open closed principle where it says that the classes should be open for extension and closed for modification.

      I think it means - that the class should provide hooks for things that could differ from its basic implementation.
    • I think of SRP as a cohesion principle, and I model cohesion as "similar things are close together and different things are far apart". This means that I violate SRP when I put different things together, which relates to "one reason to change". When I put similar things far apart, I might not violate SRP, but I have duplication elsewhere, in the form of "duplication of intent".

      As a result, I don't think directly about SRP. Instead, I think about cohesion, and when I fix cohesion problems, I tend to fix SRP violations.
    • JB, its very interesting that you think of Cohesion instead of SRP. If I'm not wrong the original intent of SRP was to achieve high cohesion. I see SRP as means to achieve high cohesion.

      At the sdtconf, we talked about Biggest Stinker, if you were to pick & chase one code smell to fix majority of your design problems, which one would it be? We had some really interesting discussions around it. Here is a summary of the same: http://sdtconf.com/wiki/tiki-index.php?page=Big...
    • I think of cohesion (in general) instead of (just) SRP (in particular). I agree that SRP is one half of cohesion, specifically the "different things far apart" part of it.

      I can't pick a single "Biggest Stinker", at least not in a few minutes, but I can give you a rough order in which I'd look for smells. The first "wave" consists of Feature Envy, Long Method and Primitive Obsession. The second "wave" includes Switch on Type and Law of Demeter Violations. From there, it depends mostly on the actual codebase.
    blog comments powered by Disqus
        Licensed under
    Creative Commons License
    Design by vikivix