Another small second-order refactoring for you today. I call it “narrow the question”.
If you’re asking an object for data, ask for exactly what you want to know, instead of what you’d need to compute what you want to know.
A very simple example: the PlayerView wants to disable/enable some of its controls when the Player is actively playing.
In the “before” code, PlayerView asks the Player for its PlayerState and decides, based on its value, whether that means the Player is playing or not.
In the “after” code, PlayerView just directly asks the Player if it is playing.
This is such a tiny change: literally cut/paste the condition out of PlayerView and wrap it in a new function in Player. Multi-class refactoring doesn’t get much simpler than this.
But the impact is real. In the Before, PlayerView has to know about PlayerState, and it has to know the meaning of Player having certain PlayerState values. In the after, PlayerView just has to know that a Player knows whether or not it’s playing.
In the actual case this is drawn from, btw, PlayerView was the only client of the chunk of code required to expose PlayerState as a readonly property on the surface of Player. Adding isPlaying to Player keeps Player from having to expose its internals altogether.
Now that Player is answering the actual question that PlayerView had, notice that Player is entirely free to change its internal arrangements involving PlayerState. Once again, as with “swap supplier and supply”, we are moving a border a tiny little bit, and gaining from it.
In many situations where a client doesn’t ask for exactly what it needs, you don’t have one client, you have many. And they all want to answer the identical question, but either they all duplicate the computation, or worse — and very common — they all almost duplicate it.
Two common places where our designs tend to do this are 1) when we’re using a generic container instead of a custom one, 2) when we’re too CRUD-centric in our thinking.
When you expose a generic container class to a client, you’re practically begging that client to perform its own computation on your data. That’s not always the wrong thing, mind, but it is a choice you want to make consciously.
When you center a design around CRUD, the same effect occurs: you are insisting your client perform complex computations around data integrity, validation, etc.
Again, there are times, down at the lowest level, where you have to do that. But you usually want to immediately re-wrap CRUD and expose only operations.
(Failure to grasp this idea has led to more failed microservice conversions than anything else. Reminder: you already have a great CRUD microservice in nearly every architecture: it’s called the database.)
What about the change-harvesting stuff? Notice three things: 1) human-ness, 2) locality, and 3) (implicit in my story) orientation.
Human change: this is all for the human. The CPU simply doesn’t care how many implementation of isPlaying are out there, and it doesn’t care where Player reveals its internal workings. Only the human does.
Local change: we’ve said locality involves multiple dimensions of neighborhood. This one is in a small neighborhood of visualization, time-to-completion, explanation, mental bandwidth requirement. It’s pretty local, in our sense, tho in practice it can have global impact.
(Implicit) Oriented change: did I really just randomly walk into this code for no reason and make that refactoring? No. I work for a living, I don’t have time for purity. This is part of a major alteration to the codebase, including not just refactoring but altered function.
First PlayerView, and everything above Player, is going to change radically, then everything below Player is going to change radically. The Player class will be the stable centerpiece.
So my first action is to make sure I understand, love, and trust Player as much as I can. This tiny change is just one piece of that.
This is important: I am not even sure this particular step is needed for the coming revolution. All I know is a) it didn’t make it worse, and b) it hid some internal stuff in Player from its clients, and c) it improved my understanding, love, and trust of the Player API.
So. “Narrow the question” is another small second-order refactoring, and it means, in effect, make sure the client is asking the service exactly what the client wants to know, neither more nor less.
A Request of the Community
Normally, this is where I plug my site. Today, and for the next while, I have a more important request. I’m GeePaw. My GeeKid, Threy, needs some financial help I can’t give just now.
If you like my work, do me a solid, and go to the GoFundMe we set up for him.
Help if you can, please.
GeePaw’s Camerata is a community of software developers leading in changing the industry. Becoming a member also gives you exclusive access and discounts on the site.