Reading Clean Code Week 6: Classes
In this week’s blog on Clean Code, I’ll be talking a bit about classes. While object-oriented programming (OOP) is an extremely vast topic unto itself that I can’t delve into too deeply here, suffice to say for now that objects are bits of code that allow us to store data and operate on that data. By utilizing OOP, we keep the details of our programs hidden, which makes our code more flexible, modular, and abstract. OOP is very useful when writing large programs, especially in the case of working on a team building a robust application.
As with any code we write, we have a better chance of success when we keep our code clean, and Uncle Bob has some useful recommendations for helping us do that for classes. To drive home some of these points on writing clean classes, we’ll design a parking lot in JavaScript. Once we’re finished, we’ll have a better sense of the functionality classes give us, as well as how to keep them clean. I want to note that there are some OOP concepts that are simply outside the scope of this blog post, particularly Encapsulation, the Open-Closed Principle (OCP), and the Dependency-Inversion Principle (DIP). Although we won’t discuss these in depth here, the linked articles will give you a broader understanding of classes.
Design a Parking Lot
Let’s be honest: You’ve probably never designed a parking lot before. Don’t let this relatively common interview question deter you. Let’s start by clarifying some assumptions about said parking lot. Good questions to ask about designing this system might include:
- What kind of vehicles are allowed to park in the lot?
- Does the lot have multiple levels?
- Can a small vehicle fit in a space designated for a larger vehicle?
- Can more than one vehicle enter the lot at a time?
By clarifying assumptions about the lot, we get a better idea of how this system is supposed to work, which will allow us to build a more efficient parking lot overall. To save ourselves a lot of discussion about how to design a parking lot in code, we’ll borrow an example from this excellent StackExchange post:
The Spot
class:
The ParkingLotManager
class:
The Vehicle
, Motorcycle
, Car
, and Truck
classes:
Now that we have the following classes built, we can create new instances of them and perform operations on them. For example, if we create the following objects:
Our program gives us the following output:
Again for the purposes of this blog, I won’t go into too much detail about the usefulness of OOP. For now I’lll just say that OOP enables us to modularize our programs, breaking them down into ever smaller pieces, reducing complexity and making it easier to solve bugs. As this is such an important concept in becoming a competent software engineer, it’s important that we’re able to keep our classes clean. Let’s see what recommendations Uncle Bob has for us.
Classes Should Be Small
As Martin writes, “ The first rule of classes is that they should be Small. The second rule of classes is that they should be Smaller than that.” If it sounds like Martin and his co-author is repeating their guidelines on function size, they are. Like functions, classes should be bite-sized and highly modularized. However, rather than measuring class size by the number of lines they take up— like we would with functions — we measure class size by the number of responsibilities it has. Let’s take a look at one of our classes from above:
As we can see, the Motorcycle
class is small both in the number of lines it occupies as well as the number of responsibilities it holds. We know that Motorcycle
inherits the id
property from the Vehicle
class, and it has an attribute specifying its size. As with functions, we introduce more opportunities for bugs to fester the more complex the class becomes. Conversely, we make classes cleaner by paring them down.
One way we might be able to keep our class sizes small is by giving them descriptive names. If you’ll recall from our review of the Clean Code chapter on Names, we should take care to give our objects designations that are clear and meaningful. You shouldn’t need myriad comments to describe a class’ responsibilities. To take a concrete example from our Parking Lot class system:
the Truck
class clearly indicates what we would expect of this class. Just by glancing at the names we can see that a truck is a type of vehicle, and that its size is one of its defining characteristics. While I know that some of the above code snippets violate this rule, in general the Motorcycle
, Car
, and Truck
classes demonstrate how small classes ought to be.
Single Responsibility Principle
One of the most concepts in OOP and in software design generally, the Single Responsibility Principle (SRP) states that a class or module should have one, single, solitary reason to change. If a class has too many responsibilities, you might refactor part of your code and forget some method still operates on another piece of logic in your codebase. With vast, all-encompassing classes, it is difficult to keep your code organized. For example, let’s look back at our ParkingLotManager
class:
Ok, ParkingLotManager
is obviously a bit bloated. For one, at 40 lines it simply occupies a sizable amount of space in our program. More importantly, though, it seems to me that we’re relying too much on ParkingLotManager
to efficiently run our system. Currently ParkingLotManager
has to keep track of space size and determine whether to add or remove a vehicle. A better system design might extrapolate the _getSizeIndex
method from ParkingLotManager
class, separating those concerns out and making our program easier to understand.
You might be thinking that creating too many classes has its drawbacks. Will I really save myself any time, you ask, if I have to scroll up and down my computer screen, scanning dozens of bite-size classes to be able to grok the whole of the program? Yet, Martin tells us, several small classes can have as many moving parts as a few large classes, and we would probably accomplish our goals more efficiently if we organized our tools into a clean, organized toolbox with ample labels for those tools. If SRP isn’t clicking for you, this blog post gave a good explain like I’m 5 explanation on the topic.
There’s a great aside in Clean Code’s chapter on classes where Martin discusses why clean code is so rare. “Too many of us,” Martin claims, “think that we are done (writing code) once the program works.” As Clean Code makes a point of saying time and time again, writing code that a computer can understand is only part of the battle. The real challenge of programming comes in writing programs that humans can understand.
Like systems in the real world, computer programs change. New frameworks and/or logic is written into our systems, with tech debt accumulating in the process. As designers of our systems, every change we introduce subjects us to the risk that the system is no longer fully functional. In writing clean code, however, we reduce the risk of those programs — and our team — failing. I hope you found this blog helpful for understanding OO design and the way you can write clean classes.