Makers Academy Day 46
This week I’ve started re-reading Sandi Metz’ s POODR as a bit of a refresher on some of the key concepts of best practices in OOD. In this post I’ll give a brief summary of the advice given by Metz in her first chapter, on designing classes with a single responsibility.
Anyone can write code, but it is a skill to be able to write code that can be easily changed in the future. Classes and the methods inside them should do the smallest possible useful thing, and have a single responsibility. Designing programs in this way means that they become akin to building blocks that can be easily reused for future unknown requirements.
“An application that is easy to change is like a box of building blocks” — Sandi Metz POODR
She outlines four key principles for designing code that boil down to the catchy acronym TRUE:
Transparent: any consequences of change are obvious
Reasonable: the cost of change is proportional to the benefits of the change
Usable: it is usable in new and unexpected contexts
Exemplary: other developers will strive to emulate the code, and perpetuate this design in future iterations
Ruby classes are representing a part of the domain. When reading through user stories, pick out the nouns and from those work out which represent objects in the domain. It can always be tempting to build a domain with objects which are included based on intuition. However, it’s important to not rely on intuition, and instead pragmatically work with the information available about the domain. Make sure the class name clearly indicates the data and behaviour of its code.
Avoid tight coupling between objects wherever possible, i.e. avoid having many classes interdependent on one another as this will create a huge technical debt that will have to be paid by some poor developer at a later stage. The design challenge is to manage dependencies so each class has the fewest possible. It needs just enough to be able to do its job but not more.
One way coupling can be avoiding is through dependency injection:
The intent behind dependency injection is to decouple objects to the extent that no client code has to be changed simply because an object it depends on needs to be changed to a different one. -Dependency injection, Wikipedia
Another is by always depending on behaviour, rather than state. Using accessor methods for instance, which hide the data even from the classes they belong to! Work on a need to know basis, and follow the Law of Demeter to make sure objects don’t know more than they should.
Finally, refactor, refactor, refactor… Continuously refactor throughout the process of building the code. Enforce single responsibility everywhere, from classes to methods. Ask each one what they do and if they have an ‘and’ that’s a clear sign that they do more than one thing and so should be refactored.
I like the idea that Metz describes of trying to talk to your class as though it were a person. Use the methods you’ve created as though they were questions, and ask the class questions about itself, to see if the design makes sense. For example asking aGear class about the size of it’s wheel, spoken out load is clear that it doesn’t make sense, and therefore needs a design rethink.
The impact of single refactoring will have a cumulative effect that will not only make the code clearer to understand in the present. It will also ensure that classes can be more easily extracted in the future because they are already broken down into the smallest possible useful block of code.
I’ll be finishing re-reading Metzs’ book this week and so will continue to explore some of her key ideas over the next few days.