class-designsolid-principlessingle-responsibility-principledecouplingcohesion

Is a class with only one function cohesive enough?


In his post SOLID: the next step is Functional, Mark Seemann states:

If you keep driving your design towards smaller and smaller interfaces, you'll eventually arrive at the ultimate Role Interface: an interface with a single method [...] If you apply the SRP and ISP like that, you're likely to evolve a code base with many fine-grained classes that each have a single method. That has happened to me more than once.

My concern is about cohesion of such classes. Does this approach drive to functional cohesion? Are those classes incohesive? Is there a bad influence on code coherence?


Solution

  • There's a great definition of cohesion presented in the book Growing object oriented software guided by tests, that states the following:

    An element’s cohesion is a measure of whether its responsibilities form a meaningful unit. For example, a class that parses both dates and URLs is not coherent, because they’re unrelated concepts. Think of a machine that washes both clothes and dishes—it’s unlikely to do both well. At the other extreme, a class that parses only the punctuation in a URL is unlikely to be coherent, because it doesn’t represent a whole concept. To get anything done, the programmer will have to find other parsers for protocol, host, resource, and so on. Features with “high” coherence are easier to maintain.

    This probably goes into the subjective territory pretty quickly, but I would argue that SRP and cohesion are both directly related and orthogonal concepts sometimes. If you have classes with only one method, then sure, it's cohesive in a sense that it does one thing only. But also, you lose something as well, ie. the class is now too fine grained to be useful by itself.

    In a functional style, having such classes makes a lot of sense. It's all about composing functions to achieve a result. C# makes this style possible, but also pretty verbose as well, so I completely agree with Seemann when he argues for F# in the case you are designing your codebase in such a manner anyway.

    The question of is this good or bad design is a subjective one, but I think we can say a few things objectively. One method classes are by their nature almost guaranteed to respect the SRP (sure, you can still miss the point and make god classes with an all powerful method). So code written in such a way should have all the benefits we would expect, ie. be loosely coupled, composable and maintainable. But there is something to say about losing the big picture of such code as well.

    I would argue that some combination of the two is required in most cases, leaning towards classes with a single method in most of the codebase. For example, you could write most of your low level code like a reusable collection of libraries, having such classes. Once you get closer to the API level, you would compose such classes to get the logic you want, and then expose such logic to your clients as more cohesive chunks of functionality. Clients would get the benefit of having a more cohesive high level code paths to follow, leading to more convenient use and greater discoverability of functionality your library supports, while also having all the benefits of having your low level code being written in such a way to be maintainable and flexible to change.