5

I've read several articles on SRP and cohesion, and they seem to contradict each other as far as low coupling is concerned.

Articles on cohesion argue that putting closely related responsibilities together in a class Highly_Cohesive_Class reduces coupling, while articles on SRP would argue that we'd reduce coupling by removing these closely related responsibilities from class Highly_Cohesive_Class into separate classes ( such that each class only has single responsibility/reason to change ).

Don't the two claims contradict each other? Namely,

BTW - I'm aware of the fact that class adhering to SRP principle is also considered highly cohesive class, but in this post the term highly cohesive refers to a class that has several closely related responsibilities.

15
  • 5
    High cohesion: Embedding closely related functionalities in a class, SRP: There should never be more than one reason for a class to change - Doesn't "closely related" imply that if they are going to change they are going to change for the same reason? How can they be closely related if not? – yannis Aug 26 '12 at 19:48
  • 1
    Yes they are not synonyms, but they are very closely related ;) "high cohesion" is not a silver bullet, what you are looking for in your design, any design, is balance. If your design satisfies both principles, you are on the right path... – yannis Aug 26 '12 at 20:10
  • 1
    Well, it depends really, how about you point us to the articles you've read so we can see exactly what the authors had in mind? – yannis Aug 26 '12 at 21:17
  • 1
    Highly cohesive classes usually reduce coupling, that's generally true (high cohesion often correlates with low coupling). SRP also can help reduce coupling, for more or less the same reasons as high cohesion, I'm not sure why or how the articles lead you to believe that SRP and high cohesion are somehow contradictory. If anything, I'd say that in a balanced design they are complementary, and in an unbalance one it doesn't matter, as you probably have bigger problems ;) – yannis Aug 26 '12 at 22:08
  • 2
    Hm, I think the source of the confusion is the use of the word responsibility. Generally speaking high cohesion aims in grouping together related functionality that contributes to a single well defined task. What SRP adds to the picture is a logical constraint: Your task is truly a single one, when there's only one reason for it to change. If what you define as "related responsibility" is in fact different (functional / logical) tasks, then your class is not a highly cohesive one, but a mess ;) There is such a thing as too much cohesion. – yannis Aug 27 '12 at 1:29
14

I think the confusion is that high cohesion does not necessarily mean you want to put all of the "closely related responsibilities" in one class, but rather that all of the items in a class should correspond to closely related responsibilities.

For example, if you have a Kitchen class, you wouldn't want bathroom logic in it; however, you don't HAVE to also have oven and kitchen sink logic in it too, just because they are related. Although it is a kitchen appliance, the Oven probably deserves its own class, and would end up relating to the Kitchen through composition. Likewise the KitchenSink would be related to the Kitchen through composition.

So, look at a highly cohesive as NOT HAVING unrelated logic in it, and look a SRP as the call to delegate responsibilities to objects that serve that one responsibility.

4
  • hi, I understand the point you are making, but there may still be cases where we decide that highly cohesive class may have several responsibilities instead of just one. Now will in such cases this class usually be more or less coupled than if we break it into several SRP classes, and why? – user1483278 Aug 26 '12 at 23:33
  • 1
    @user1483278 You need to give us an example of what exactly you mean by "highly cohesive class may have several responsibilities instead of just one" (preferably by editing your question). Responsibility is such a wonderfully vague word, and quite possibly the source of the confusion in your question. – yannis Aug 27 '12 at 1:53
  • hi, I don't have any specific examples, since thus far my study of patterns is purely theoretical – user1483278 Aug 27 '12 at 21:00
  • Neither a kitchen nor a bathroom should "be" a sink, but it would seem better for a kitchen and a bathroom to implement a LiquidDisposer interface than to require clients to search the room for an object capable of disposing of liquid [e.g. a sink]. Even if all the actual functionality of a kitchen is handled in other classes, just the bindings between the interfaces a kitchen should support and the encapsulated objects that implement them would be fairly substantial. – supercat Dec 23 '13 at 22:01
2

Uncle Bob states in his book that this might be one of the least understood principles. I've seen lots of evidence of that in my career.

From his clean code blog: The Single Responsibility Principle

Another wording for the Single Responsibility Principle is: Gather together the things that change for the same reasons. Separate those things that change for different reasons. If you think about this you’ll realize that this is just another way to define cohesion and coupling. We want to increase the cohesion between things that change for the same reasons, and we want to decrease the coupling between those things that change for different reasons.

However, as you think about this principle, remember that the reasons for change are people. It is people who request changes.

he also defines it as:

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

Yet another way to say this is that an SRP compatible component is one that when it does change it always changes with respect to its ability to fulfill a role for a specific audience. As a unit these functions satisfy a single need. Depending upon the language you use 'component' could mean a single package, class or file.

for example: consider a class that implements stove (from the post above) would have inputs (electricity/natural gas/propane) and outputs (BTUs and burner configurations and other features like self cleaning).

All of this stuff is related to the single job of cooking food and the cook is the single party that cares about combining these functions to achieve their goals. The SRP indicates that it is preferable to put all of these features in a single component. Some may be exposed and others completely internal.

The point is that there is very little reason to split these aspects away from the stove as they relate to the stove and to nothing else. Any cook can control these aspects through the stoves interface, the knobs and burners. Every cook wanting to use the stove will have exactly the same concerns and benefit from the interface in the same way. Because these functions are all inseparable from the stove (it isn't as useful without them and they don't apply to anything else) it is wise to collate them within it because they have high cohesion.

A frying pan on the other hand is a much smaller component that can be used with the stove but is not an integral part of it. When you replace the stove you don't necessarily have to replace the frying pan.

The pan may have a simple interface like: fry ( foodItem ); stop();

FoodItems might only have: isDone();

So some components can be large and others small and where the line is drawn depends upon the component's usage which is why I think there is so much confusion about the principle.

In an oversimplified but more pertinent coding-related example the one 'person' may be a developer that wants to access files or something like that. Here the single purpose is file I/O and the 'audience' is developers. They probably expect a useful file class to have several related functions on it like:

a = file.read()
file.seek(c)
file.write(b)
file.close()

These functions have high cohesion because they are intimately related to each other with respect to the file object the class represents. They make the file object highly useful and internal aspects of the file that the outside world need not be concerned with are encapsulated.

one could separate them into separate classes and pass a file object (or worse, the file name) into them. i.e. something like:

a = fileReader(file);
pos = fileSeeker(file, c);
fileWriter(file, b, pos);
fileCloser(file);

These objects "do one thing" but because they all operate on a file object and they are independent of other purposes splitting them up clutters the namespace for no gain. Note also that by turning the object 'inside out' the file position (pos) must now become an additional concern for the outside environment.

This supposed 'application' of the principle would really in fact be a 'violation' of it. There are many well-meaning but incorrect examples out there that would lead developers to the wrong conclusion. I think this is why Uncle Bob says it is one of the poorest understood of the SOLID principles.

It isn't very cohesive to break up a component that performs interrelated operations on the same object(s). It should only be done if there are multiple classes of 'consumers' that depend upon the the same feature set but to achieve different ends.

1
  • I agree that SOLID is completely misunderstood. This is a good answer to elucidate how it is supposed to be applied. – user949300 Feb 19 at 1:10
1

You are mixing up the two. Responsibility is about behavior, about what a class should do or be capable of. Cohesion is more about components, about grouping classes that operate within the same domain. So you could have a class to contain some related data and another class to retrieve that type of data from a store. The data members in the class would be cohesive because they have little meaning on their own, they have meaning together (may come from the same table). And the classes could be in the same assembly because they both deal with offering access to data, which would make them cohesive.

Having separate classes for containing data and for fetching/storing data would comply to SRP. At the same time putting them in the same assembly would recognize their cohesiveness. You want to separate them yet keep them close together because they need each other.

The components in a car are highly cohesive, they work together. Yet it is a good thing the steering mechanics are separated from the gear box so you can maintain the two separately and modifying one will not impact the other. At the same time you as a driver want access to both when you drive so it is nice they are integrated in the same vehicle.

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