Over Thanksgiving break, we were asked to make a playlister app. For the playlister, we needed to create Song, Artist, and Genre classes, which were all highly dependent on each other. Songs included the artist and genre objects. Artists included the song object and an array of genres. Genres included an array of songs and an array of artists. Everything was dependent on everything else. It was our first project using multiple classes and got complicated quickly. On the heels of that, I re-read Chapter 2 of Sandi Metz’s Practical Object-Oriented Design in Ruby last night, the chapter on Single Responsibility. Having the experience of building the playlister app helped solidify the concepts Metz was explaining, and helped me understand their importance.
The Single Responsibility Principle is the philosophy that any aspect of your code should have one purpose, and the code contained in that class, method, whatever, should only serve to meet that purpose. Anything that does additional work beyond this scope should be separated out and made its own class or method. This helps you keep reusable chunks of code, and reducing the dependencies helps you keep your code stable when things change.
One of the challenges with single responsibility, however, is that it can be costly to restructure your code, and so in order to help you stick to single responsibility practices, or move to them when it makes sense, your code should be easy to change (or TRUE, if you’re a fan of acronyms). This advice leaves us with two questions. First, how do we know if something is filling a single responsibility? Second, how do we write code that’s easy to change?
Meitz suggests a few ways to determine if your code adheres to the single responsibility principle. She suggests going through and asking each method if it applies to your code, keeping in mind what the purpose of the class is. If you put the purpose of the class in a sentence and the sentence uses “and” or “or”, there’s a good chance you’re not sticking to a single responsibility. It can become more difficult to define a single responsibility as projects get more complicated, but with complexity the importance of maintaining single purpose principles also grows, so that your code can remain adaptable, clear, and accessible to others.
Regardless of if your class takes full advantage of the single purpose principle, it is also important to keep your code easy to change, because it inevitably will. This helps prevent code from breaking if changes are made to a separate section or class, and also increases your ability to re-use pieces of code. There are several suggestions Metz makes to help guide you towards making easily-changeable code. Some of these are simple, such as taking advantage of the attr_accessor and the like to wrap all of your instance variables in classes, separating the data from the behavior. By using methods instead of the instance variables themselves, you only have to change your data in one place if the value changes, instead of having to scour your code for all references to it. Metz suggested a slightly more complicated use of Structs to hide complicated arrays and data structures in methods as well.
Now, moving on, my next project is making Conway’s Game of Life. I’ll be sure to keep Metz’s principles in mind as I go through it.