Object-Oriented Sandwiches — Part 4
Welcome to the final installment of a series where I’ve attempted to explain Object-Oriented Design (OOD) through the metaphor of a fictional sandwich shop. In this part we’ll learn about organizing your code to make it easier to fix and expand.
These examples build on the previous posts, so if you haven’t read them yet, start with part one.
Welcome back to the Rubytown Sandwich Shop
Inspired by how well their “expansion” to a four-person shop is going, Bob and Jane are taking a long look at things to see if there’s anything else they can do to make their successful business more successful.
All their bread comes from a local bakery, and it’s decent quality, but there have been botched orders and sometimes the deliveries are late. The sandwich shop came with a bread oven, but learning to bake bread was too big a challenge when they started out. Now that they’re feeling good, Bob and Jane decide to try baking their own bread on-site to reduce waste, control quality and reduce costs.
There’s only one problem: Bob’s a control freak, but he’s not a neat freak.
Since Bob was the only one in the kitchen until Edward showed up, there’s no rhyme or reason to where anything is. Things are more-or-less where Bob set them down the first time he started making sandwiches: condiments are mixed-in with the meats, half-empty bags of buns lay scattered around the counter all day and the sandwich wrap is on a shelf under the counter where Bob has to bend over to get one for every sandwich. Bob can get things done because he’s acclimated himself to the chaos.
But adding bread-making to the mix pushes the chaos over the edge. Edward tries to make sandwiches while Bob rolls out bread on the same counter, getting flour in the sandwiches and mustard in the dough. They bang elbows. There’s a lot of shouting. Productivity goes down. Bread gets burned.
First organize, then expand
The reason Bob and Edward are having trouble is because the prep area is violating the Single Responsibility Principle. Making sandwiches and making bread are different tasks with different demands and time frames. The “stuff that lives in them and stuff that happens in them” are incompatible. They should be split out and the areas organized to make both jobs easy, not just possible.
The process of reorganizing your code to make it easier to work with without changing its behavior is called “refactoring”. It’s a completely-separate tasks from fixing bugs or adding features. Don’t ever try and do more than one at a time, because it’s easy to get tangled up.
So how does Bob (and Edward, because the kid does have a good idea on occasion) refactor the kitchen?
Separate the Responsibilities
First, they physically separate the tasks. They spend an evening building a dedicated bread prep area away from the sandwich prep area and right next to the oven. Moving the bread-making equipment to this new area immediately frees up the sandwich area to return to what it does best. An unexpected benefit is that transporting bread in and out of the oven is quicker and no bread gets dropped on the floor in transit.
In an application, we would identify data that is “bread-making specific” and actions that only bread-making requires and move it into a dedicated class that can be created and accessed independently of the sandwich-making class. Now when we work on the bread-making code we don’t have to wade through a bunch of unrelated sandwich code to find the bits we’re interested in, and vice-versa.
Just this one optimization is often all we need to get our code back under control. It’s more understandable because there isn’t the “noise” of the other code. It’s also smaller, which means it’s easier to keep the whole thing in our head while we’re thinking about how to fix bugs or add features.
While they’re dragging things around, Edward pulls the sandwich wrap off the bottom shelf and puts it at the right edge of the freshly-cleared counter. He expects Bob to object, but Bob nods his head. Secretly, his back is killing him and he’d been meaning to move that stack for months, but there wasn’t a good place to put it with all the other equipment in the way.
They realize that sandwiches are always constructed in the same order:
- bread (buns or slices)
They start sliding things around the counter until the ingredients for each of these tasks are next to each other, arranged in a left-to-right order, ending under the order window. When the lunch rush comes, they are making sandwiches at twice the speed even Bob could manage before. An unexpected benefit is that they can split the sandwich process into phases and have two people working on the same sandwich. Bob grabs the bread, slaps on the meat and cheese and slides the sandwich and the order slip to Edward, who adds veggies and condiments, then slices and wraps it before putting it back in the window. Bob can’t remember the last time he smiled making sandwiches, but he smiles a lot today.
The bread and sandwich area do have one thing in common: the finished bread and buns. Bob put the new storage racks next to the bread prep area, and now both of them spend a lot of time during lunch rush sprinting across the kitchen, ferrying bread back to the sandwich area and stuffing it into identical racks there.
Edward suggested putting all the racks between the two areas, so bread goes in from one side and comes out the other. No more sprinting, no more dropped bread and they can tell at a glance exactly how much bread they have left.
In programming, the acronym DRY stands for Don’t Repeat Yourself. One of the main focuses of refactoring is finding places where we’ve written essentially the same code in two places. In code the main drawback is that when we have to change the code, we must remember to do it in both places or the two parts of the program won’t behave the same. If we extract the duplicated code to a common location and have both instances talk to it, this problem goes away. Besides, if the same code appears in multiple places, it’s probably not the core responsibility of any of the things it’s wrapped in and is better off in a place of its own.
The racks are now the “bread storage area” and both the sandwich area and bread prep area can “talk” to it.
Refactor the Pain Away
The sandwich shop is doing great and the kitchen is in better shape than ever. Bob has refactored the kitchen to accept the new bread-making operation and allow multiple workers on the sandwich line, but he hasn’t done anything “just because”. He’s a businessman and a busy man, so he only reorganized the kitchen when circumstances demanded it. Certain things about the workflow became painful, so he refactored to make the pain go away.
If you’re not feeling pain from your code, the design is good enough for now. The three most-common pain points are:
- It’s difficult to think about what the code does
- It’s difficult to add features
- Lots of bugs get through because you can’t see the edge cases clearly
The Rubytown Sandwich Shop Success Story
Bob and Jane are doing just fine with their shop. The improvement in the bread has brought in even more customers, and now that the kitchen has been reorganized, making sandwiches and bread is simple enough that hiring more people to help out isn’t as scary as it was. They’ve even talked about adding pastries and Jane’s home-made potato salad to the menu, which will mean more expansion back in the kitchen area, but now that they know how to organize and grow their business in a sustainable way, the future looks bright.
- Refactoring is improving the structure of your code without changing its external behavior.
- Organize your code to make things easy, not just possible.
- Refactoring, adding features and fixing bugs are different things: do one at a time.
- DRY: Don’t Repeat Yourself
- Separating responsibilities and eliminating duplication (DRY) are two of the main focuses of refactoring.
- Follow the pain when deciding what to refactor, then refactor the pain away.