Poorly cohesive classes are difficult to understand and maintain. They’re harder to debug and more difficult to extend. The bottom line is the more issues a class has to deal with, the more things can possibly go wrong.
Classes that have poor cohesion end up becoming overly complex. They couple multiple issues together that shouldn’t necessarily be coupled together.
Classes become uncohesive when we don’t take the time to call out the entities for which the behaviors belong when we’re building out behaviors. You might think this is no big deal but it adds up. Without calling out in the domain, the object model becomes distorted and it becomes harder to understand and maintain in the future. And my number one goal when writing code is to communicate to other developers what it is I’m doing, so building out the object model and naming the elements well is fundamental to building good software.
One way to create cohesive classes is to look for behaviors that share or affect a common set of data. The common set of data can become instance data for the class, and the behaviors can be methods on the class. If cohesive classes are about having a single responsibility, then cohesive methods are about fulfilling some functional aspect of that single responsibility. Methods are the behavior of classes and they should be clearly defined.
Poorly cohesive methods are methods that may have overly generalized interfaces. For example, many years ago I worked on the OS/2 operating system and we believed that having a generalized API was a good thing. It minimized the number of APIs you needed to provide and it seemed to be making more efficient use of memory, which was quite expensive back then.
Today I believe just the opposite. Overly generalized method interfaces give orthogonal clients access to the same APIs. This means they become a coupling point for many different types of clients and can create side effects between different clients and couple them together in unexpected ways. Methods that try to do too much are also very difficult to use.
Poorly cohesive methods are also very difficult to refactor. Extract Method is the most common refactoring that I see aside from Rename, which is my favorite refactoring because it’s safe and it embeds knowledge into the system. Extract Method is my number one defense against poorly cohesive methods, which are simply methods that are trapped within a loner method. If I can name what the behavior does then I’ll extract that behavior into its own method and give it that name. I find that this makes my code far easier to read and understand. I much prefer to delegate to a private method with a meaningful name than to insert a comment explaining what the block of code does.
Sometimes bad things can tell us good things and I think this is true with cohesion. When we look at the price we pay for poor cohesion in code, we see that it’s simply not worth it. It’s far better to take the time and craft method interfaces for specific types of clients and to craft our object models with entities that are focused and singular.