This post is the ninth of a series adapted from my conference talk, Fun, Friendly Computer Science.
When you push a button on your remote control, something happens on your TV. The volume goes up, the channel changes, etc. There are few, finite things you can do with a remote. And you don’t need to know how it works to make your change happen. For example, you don’t need to know how infrared light works, how the binary is encoded or decoded, or how the microprocessor in your TV acts.
This is an abstraction. You push a button and a thing happens. And you only have a few buttons to choose from.
Abstraction is an extension of encapsulation. Abstraction refers to hiding all the internal implementation details of a class and providing very few, clear mechanisms for other objects in the code to interact with each other.
If were to pseudo-code the interaction between a remote control and a TV (using Javascript), the result might look something like this (disclaimer: I am not an electrical engineer and do not understand the guts of how a television and remote control work, this is a rough approximation. I’m counting on the fact that you are also not an electrical engineer 😉)
const Remote = new RemoteControl();
let tvState = Remote.power();
renderTV(tvState);
tvState = Remote.turnUpVolume();
renderTV(tvState);
tvState = Remote.turnUpVolume();
renderTV(tvState);
tvState = Remote.turnDownVolume();
renderTV(tvState);
What you don’t see is what is happening behind the scenes. When you push one of the buttons on the remote, it handles the button click by encoding the data into binary, converting that to infrared light, and then sending that to the television. When the television receives the light data, it unpacks it and its microprocessor takes care of making the necessary changes to the state of the television and displaying that state back, to you.
export default class RemoteControl {
constructor() {
this.television = new Television();
}
power() {
return handleButtonClick(PowerButton, this.television);
}
turnUpVolume() {
return handleButtonClick(VolumeUp, this.television);
}
turnDownVolume() {
return handleButtonClick(VolumeDown, this.television);
}
}
function handleButtonClick(button, television) {
const encodedData = encodeButtonPressIntoBinary(button);
return sendBinaryDataAsInfraredLight(encodedData, television);
}
function encodeButtonPressIntoBinary(button) {
console.log("Assume the button data is encoded into binary");
return { button: button };
}
function sendBinaryDataAsInfraredLight(binaryData, television) {
const infraredLight = convertBinaryToInfraredLight(binaryData);
console.log("Sending infrared light signal");
return television.handleRemoteControlClick(infraredLight);
}
function convertBinaryToInfraredLight(binaryData) {
console.log("Converting binary data into infrared light");
return { lightData: binaryData };
}
Extending the television metaphor, think back to the 50s, 60s, and 70s when a TV didn’t have a remote control. To turn down the volume, you had to stand up, walk over to the TV, and turn one of the knobs on the front of the TV. This is a different abstraction, a different way that an object (in this case, the user) interacted with the television. We realized in the 80s that it would be better if we could point a little thing at the TV that would send light to tell the TV what to do so we never had to get off the couch.
When programming, if someone tells you that you made the “wrong” abstraction, what they mean is that the interface for how other objects use your object doesn’t quite fit the requirements. This often happens when you abstract and simplify your code too early to truly know how other objects will want to use it. It’s not a big deal, we all do it. Often, you don’t need to re-do all of your code, just the layer between your object and its calling objects (method signatures, inputs, outputs, etc.).
Top comments (0)