Espresso Yourself: Explain Object-Oriented Programming to Your Barista
Adam Schmideg
Posted on December 14, 2023
"How many times do you have to draw a heart with milk foam before you get it right?" I ask.
The barista at my local cafe gazes upwards, pondering. "Took me about thirty, maybe fifty tries," she finally says. "The thing you really need to focus on is..." She then dives into a brief yet insightful explanation about this particular skill in the art of coffee making.
Sharing your enthusiasm with others can double the joy. But when you can't, it often leads to a sense of loneliness.
Alright, now it's my turn to share something. It's time for me to explain object-oriented programming to her.
Not all dates are the same
Big ideas like Darwin's evolution and Einstein's relativity theory often remind us of what we feel we should know. But honestly, I haven't delved into these topics deeply. Books on such subjects gathered dust on my shelf, unopened.
Then came Object-Oriented Programming (OOP). Intrigued, I bought a book about it, only to be baffled by jargon like classes, objects, and inheritance. The more I read, the less I understood, feeling lost in a loop of concepts. Frustrated, I shelved the book next to an unread tome on quantum theory.
It was a standard application – the kind with windows, text fields, dropdown menus. There was an OK button that set things in motion and a Cancel button that, predictably, did not. To a user, it was as ordinary as any software they'd encounter in their daily digital life. But for me, this was new ground, my first functional software from the initial line of code to the last.
The palette in the editor was like a toolbox filled with all the gadgets I needed. If I ever needed something new, I could theoretically create it, but figuring out how to add a new component to that palette was a skill way beyond my level – that was advanced stuff, meant for the pros.
Then, a colleague pointed out something: the system I was working with was built on object-oriented programming principles. The text fields, the buttons, the windows – they were all "objects."
I revisited the OOP book, and while some parts still baffled me, others began to click. Yet, it mostly echoed what I had already figured out using the program's components.
As I worked on the application, I initially convinced myself that the pre-installed components were sufficient. But deep down, I knew this wasn't entirely true, and I had to face that reality. A key feature of the application allowed users to select a date range – something you'd typically do when booking a hotel room or a flight, choosing both a start and an end date. The challenge was, there wasn't a single component designed for this specific task.
What I had at my disposal was a basic date selector. To create a makeshift solution, I placed two of these side by side – one labeled as the 'start date' and the other as the 'end date'. It was a quick fix, but far from ideal. It was clunky and not user-friendly.
This approach was also a headache for development. Every time I needed a date range selector elsewhere in the application, I found myself copying and pasting this pair of date selectors. This process involved not just the components themselves but also tweaking the associated code to fit the new context. It was tedious and error-prone. More than once, I caught myself in a situation where, if I missed updating a reference in the code, it led to confusing outcomes. Imagine booking a flight and then a hotel room, and then when you decide to change the end date for your hotel stay, you accidentally end up modifying your flight schedule instead. It was clear that this makeshift solution was not just inelegant; it was fundamentally flawed.
Meeting Nemo
In the deepest point of my journey, the kind where you might expect to meet a wise man, I met Nemo. But he was far from the archetypal sage in appearance. He was no captain from a Jules Verne novel, nor a fish, nor did he fit the typical image of a hero. Nemo was a bulky man in his mid-thirties, with a bald spot that he seemed to casually, almost unintentionally, try to cover with strands of his uncombed hair. At first glance, he didn't strike you as the kind of person who'd make a profound impact, but as I would soon learn, appearances can be deceiving, especially in the world of programming.
From self-appointed gurus like Steve Jobs, you often hear that the key to success is focus. The mantra is simple: concentrate on one thing and one thing only. Everything else, no matter how tempting or appealing, should be disregarded if it doesn't align with that singular focus. This philosophy advocates for a laser-like concentration, cutting away distractions and peripheral interests to hone in on what truly matters.
Nemo was different, maybe even the opposite of what you'd expect from the Steve Jobs type. Back then, Jobs wasn't the big name he is now, and I doubt Nemo paid much attention to his ideas about focusing on just one thing. Nemo was all over the place, in a good way. One week, he'd be all about script languages, telling everyone how great they were. Then, a month later, you'd find him talking up the serious, type-safe languages. He was always trying something new at work. He'd put together a rough prototype of an idea, get everyone excited about it, and then, just like that, he'd be onto something else. He was like a kid in a candy store, always looking for the next sweet thing to try.
The first time I met Nemo, he was really into object-oriented programming. It was unusual for him to stick with one thing for so long, but he was really excited about it. He was so into it that he put together a whole day workshop, just to talk about it and show everyone what he could do with it.
Making coffee the object-oriented way
Nemo kicked off the workshop with something unexpected. He pointed to the coffee maker in the room, a pretty ordinary-looking machine that you wouldn't give a second glance normally. But there was something about the way he looked at it, full of excitement, that made us all take notice. It wasn't anything fancy – just your regular coffee maker, a bit worn from use. Yet, I still remember exactly how it looked, because that day, Nemo turned it into our first lesson in Object-Oriented Programming (OOP), showing us how even the most everyday things can open up a world of complex ideas.
The Generic Idea of a Coffee Maker (Class): In OOP, we start with a 'class', which is like a blueprint. (Think of it as the Platonic idea of a coffee maker if you are philosophically inclined.) This class defines what a coffee maker is and what it can do. It doesn't represent a specific coffee maker in your kitchen but the general concept of what any coffee maker should be like.
Capabilities of the Coffee Maker (Methods): In our coffee maker class, we define 'methods', which are the actions that any coffee maker can perform. For example, we might have methods like addWater()
, addCoffee()
, or other actions like turnOn()
and turnOff()
. These methods are like instructions that tell the coffee maker what to do.
The most important method might be makeCoffee()
. When this method is called on a coffee maker object, it uses the water and coffee added to brew a cup of coffee. It’s like giving a command to the coffee maker to perform its main function.
Continuing with Nemo's explanation, he delved deeper into the workings of the coffee maker as an example of Object-Oriented Programming. "If we peek inside the makeCoffee()
method," he explained, "you'll find that it's calling other methods like brew()
, getWater()
, and getCoffee()
. These are the coffee maker's internal processes, its own business."
He emphasized that in OOP, this is a key concept. Some capabilities of an object, like our coffee maker, are exposed to the user – these are the methods you can interact with directly, like addWater()
or makeCoffee()
. These are akin to the buttons and levers on the actual machine. However, there are other methods, like brew()
, which are part of the coffee maker's internal mechanics. They're crucial to how the coffee maker works, but as a user, or in programming terms, as someone using the coffee maker object, you don't need to worry about these internal methods.
"This distinction," Nemo continued, "is about what the user (or other parts of the program) needs to see and interact with, versus what the object manages internally. It's like knowing you can press a button to start the coffee maker, but not needing to know exactly how the heating element works or how the water is pumped through the grounds. In OOP, we encapsulate these details, exposing only what's necessary and keeping the rest tucked away as part of the object's internal implementation."
Nemo then posed an intriguing question to the group, "But what happens when you press the 'make coffee' button? How much coffee does it produce? And what will it taste like?" He smiled as he saw the puzzled looks around the room. "You see, these questions can't be answered by the coffee maker itself. From its perspective, those details are somebody else's problem."
Somebody else's problem
He explained further, "When you call the makeCoffee()
method on our coffee maker object, it doesn't just start a process; it actually returns another object – a coffee object. Yes, in the world of OOP, the coffee it makes is an object as well."
Nemo continued, "This coffee object has its own methods. For example, there's a getServings()
method, which returns the number of servings the coffee maker has produced. And there's another method, getTaste()
, which might give you an idea about the flavor of the coffee. These methods belong to the coffee object, not the coffee maker. It’s a way of organizing our program so that each object has its own responsibilities. The coffee maker makes the coffee, but the coffee object itself can tell you more about its quantity and quality."
Nemo shared a personal anecdote to further illustrate his point. "I have a coffee maker at home," he said. "It's similar to this one, but there are some key differences." He described his own machine, an expensive Italian model, explaining that unlike the one in the workshop room which used ground coffee, his at home took whole roasted beans and ground them itself. However, he pointed out, the process of adding water was identical in both machines – each had a container that needed to be filled with water.
"This brings us to a crucial decision in programming," Nemo continued, addressing the room but somehow, it felt like he was speaking directly to me. "When coding for these coffee makers, you have two options. The first is to copy the addWater()
code from one coffee maker to the other. But as some of you might know," he glanced in my direction, "this approach can be tedious and prone to errors." I nodded, thinking back to my struggles with the date selection feature.
"But," he went on, "programmers have a principle they like to follow – it's called DRY, or 'Don't Repeat Yourself'. The idea is to avoid repetition wherever possible." He then introduced a concept that was new to me but made immediate sense – class inheritance. "You can have a general CoffeeMaker
class that includes the addWater()
method. Then, you can create subclasses like SimpleCoffeeMaker
and FancyItalianCoffeeMaker
. Both of these will inherit the addWater()
method from the general class, and it will work the same way in both."
Nemo then explained a subtlety about the makeCoffee()
method. "In the general CoffeeMaker
class, makeCoffee()
is just a placeholder. It doesn't actually do anything by itself. The specifics of how coffee is made differ between machines, so it's up to the subclasses to define their own makeCoffee()
method." It's somebody else's problem again.
The date range selector
Nemo's explanation opened my eyes to a new way of seeing not just programming, but the world around me. He showed us that virtually anything could be modeled in terms of objects and methods within the framework of Object-Oriented Programming. It was like discovering a new language to describe and interact with the environment around us.
"There are hierarchies of classes," Nemo continued, his enthusiasm infectious. "In these hierarchies, classes inherit methods from their parent classes. This is the beauty of OOP – it mirrors the natural world. Just like in families where children inherit traits from their parents, in programming, subclasses inherit methods from parent classes."
"And remember," Nemo emphasized, "all these methods we talk about, they operate on objects. An object is an instance of a class, and it's the cornerstone of OOP. It's a self-contained entity with its own attributes and behaviors."
As Nemo elaborated on these concepts, I began to see the power of this approach. By breaking down complex systems into smaller, manageable objects, and by establishing a clear hierarchy where methods and properties could be inherited, we could model the whole world.
After that workshop, I went home with my head full of new ideas. I had always thought that adding a new component to the Delphi palette was some kind of high-level magic, something way beyond my skills. But that night, I sat down and went through the entire Delphi documentation about adding components. I read and read, and as I did, everything started to fall into place. By the time it was midnight, what had seemed like magic before now made perfect sense. I was so pumped up with excitement that I could hardly sleep.
The next morning, I got up way earlier than usual, around 7 am, buzzing with energy. I got straight to my computer and began working on creating my own date range picker component. Within just a few hours, I had it figured out and added it to the Delphi palette. It felt amazing. This task that I had once thought was too complex for me was now done, clean and efficient. No more moving code around from one place to another.
Posted on December 14, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.