I love the fact that people are working to bring more advanced object-oriented programming techniques to WordPress-related development.
That is, I’m really glad to see others are pushing for people to write more truly object-oriented code rather than using classes as a “poor man’s namespace” (ht to Franz for that nickname), or doing things like setting up hooks outside of a constructor.
To be fair, I’m as guilty as the next for not always writing very good object-oriented code in the context of WordPress (though I’m slowly working to change that – hopefully the Plugin Boilerplate is proof-positive of that), and I think it’s something that we should all be striving to get better at doing.
The Single Responsibility Principle Defined
One of the things that we often here is that classes and functions should do “one thing and do it well” and, many times, people attribute this to the Single Responsibility Principle. The thing is, although the idea behind what are our classes and their functions should do is great, it’s not really what the Principle states.
From 8th Light:
The Single Responsibility Principle (SRP) states that each software module should have one and only one reason to change.
Martin defines a responsibility as a reason to change, and concludes that a class or module should have one, and only one, reason to change. As an example, consider a module that compiles and prints a report. Such a module can be changed for two reasons.
And I think there’s difference between saying that a module should “do one thing and do it well” and “have a single reason to change.” Sure, the different is subtle, but let’s contextualize this idea to WordPress.
Let’s say that you’re writing a plugin that’s responsible for adding a custom post type. Along with the custom post type, additional scripts, styles, meta boxes, and data validation, serialization, and retrieval code must be written.
I think we’re able to convince ourselves that our class does “one thing” by saying “This class is responsible for creating a custom post type.” Literally speaking, the class:
- Registers several actions (and maybe some filters)
- Loads stylesheets
- Creates additional views for meta data
- Handles serialization and validation for the post type and its meta data
Obviously, this is more than one thing. Instead, it looks as if we need to have classes for:
- Coordinating hooks with their callback functions
- Loading stylesheets
- Displaying the custom post type administrative view
- Displaying the custom post type public view
- One class to represent each meta box
- A class to handle validation
- A class to handle serialization
- …and so on.
Yes, this looks a little daunting at first, but the first two objections to this approach are usually easy to put to rest:
This is creating a lot of classes to do a simple thing.
If it were a simple task, then it wouldn’t necessarily warrant the need for a discussion about it. Instead, just because we know how to do something doesn’t mean that it’s simple. It still requires work to make common things happen; software has moving parts and each part should be easily identified.
Secondly, this is also an issue of maintainability. Having a lot of small classes make it much easier to maintain a codebase over time. This is significantly better than having a monolithic class – or god-class – that “creates a custom post type (and does some other stuff, too).”
All of this code spread across so many files rather than the code in a single file?
Absolutely – and that’s really the crux of the Single Responsibility Principle. No matter how you look at it, you’re likely going to have a significant amount of code – it’s either going to be in one large file that arguably results in poor maintainability, or you’re going to have it spread across multiple files each of one has a clear purpose and responsibility.
And when it comes down it, I think we’d all really prefer the latter.
Ask It Another Way
Anyway, this is clearly not a very in-depth discussion about the Single Responsibility Principle and much, much more could be said, debated, shared, and argued. The main takeaway that I’m sharing (primarily for myself, to be honest) is this:
Rather than ask:
Is this module doing one thing and doing it well?
We should be asking:
What would require this module to change? Or, rather, under what conditions what this module need to be changed?
Until we have a single, definitive answer for each function and its class, then we’re not properly employing the Single Responsibility Principle.