A poor OOP example
Sometimes you come across OOP examples that demonstrate the modeling of a real-world object, like a vehicle for example. Below is an example which is made up by me but representative of such examples.
class Vehicle def initialize(fuel_tank_capacity, current_fuel_level) @fuel_tank_capacity = fuel_tank_capacity @current_fuel_level = current_fuel_level end def fuel_level_percentage @current_fuel_level.to_f / @fuel_tank_capacity end end vehicle = Vehicle.new(20, 15) vehicle.fuel_level_percentage # 0.75
To me, OOP is all about modeling. A model, the way I define it, is a piece of code that represents a part of reality in a simplified way in order to make that code easy to understand and work with. The above is a poor OOP example because it’s not a model. The example doesn’t try to model a vehicle, it tries to simulate it.
The inappropriateness of the above example can be made more obvious if you ask yourself: what kind of program could the above code be part of? If we’re talking about web development, I can’t really conceive of a web application that would need to monitor the current fuel level of a vehicle (as in the exact amount of actual fuel that’s in the tank at any given moment).
For what purpose could a web application be keeping track of a vehicle’s fuel tank capacity and current fuel level? I can’t think of any (non-crazy) web application that would actually somehow monitor a vehicle’s fuel level in real time or why we would need such a thing. The above example is not a very realistic one.
I could imagine someone saying, “Sure, the above example isn’t very realistic, but it’s at least good for teaching purposes.” I don’t think so. If we’re going to teach something, I say let’s try to teach it realistically. Examples like the above completely change the character of what’s being discussed and make it hard to imagine how OOP concepts can be applied to real application code which looks and feels much different from the above code.
A more realistic (but still not great) OOP example
Even though I can’t think of why we would ever need real-time fuel level monitoring in a web application, I can think of a reason why a web application might need to track vehicles’ fuel levels more generally.
Let’s imagine a program for managing a car rental shop. The car rental shop needs to keep track of their vehicles’ fuel levels so they know how much fuel their customers might owe them.
I’ve never worked at a car rental shop but I imagine that they don’t typically use telemetry to constantly monitor fuel levels. They probably just look at the fuel gage periodically and make a note. The “make a note” part could probably be achieved with a model like the following:
class FuelReading def initialize(datetime:, level:) @datetime = datetime @level = level end end
Unfortunately this example isn’t very interesting from a teaching perspective. There’s data but no behavior. You put in the datetime and observed level and you’re done.
I suspect this is one reason why we sometimes end up seeing such contrived OOP examples. Most web applications are very database-oriented. Their code deals mostly with shuttling data to and from the database. It takes kind of a long time before the logic starts getting sufficiently complicated for the principles of OOP to start making a difference. And by that time we’re way past the point of something that’s simple enough for a blog post or book.
So even though I think this example is a more honest one than the first, it’s not very useful. Here’s an even better one.
A better OOP example
Here’s an OOP example I came up with that I think meets the qualifications of a) is a real model, not a simulation and b) has some interesting behavior (not in the sense of “oh, that’s interesting” but in the sense that there’s more than zero behavior needed in order for this object to make sense).
What this class models is the representation of a word when you’re playing the game Hangman. The representation of the word starts off as just a bunch of blanks, and then the blanks get converted to actual letters as the player makes correct guesses.
Here’s the code:
class Word def initialize(value) @value = value end def cloaked(correct_guesses) @value.split("").map do |letter| correct_guesses.include?(letter) ? letter : "_" end.join end end word = Word.new("cat") puts word.cloaked(["c"]) # c__
This is a good example because it could actually be part of a real program. It’s not a simulation. It’s a model of a word, meaning that we take the real-world concept of a word and only express the aspects of a word that we care about with respect to our Hangman game.
More bad OOP examples
Just to show you that my argument isn’t a strawman, I googled “oop examples” to see what came up.
The first result discussed a
Car class with a single method,
repaint. Nope. The author of that post was thinking simulation, not model. A web application never repaints a car.
This same post later has a
Dog class with a
bark method. Again, web apps don’t make dogs bark.
The next example I came across was a
Parrot class with
dance methods. Another simulation.
The next was an
Animal class with
eat methods. People love these terrible examples!
Why this matters
My gripe of “a model is not a simulation” might seem like a pedantic nitpick. However, I personally had been learning OOP for over fifteen years before I consciously realized that a model is not a simulation. Even then, I didn’t come to that realization on my own, I read it somewhere. (I don’t remember the original source, unfortunately.)
I think part of the reason why so many developers including myself are so slow to learn OOP is that we’re raised on unrealistic examples like
car.repaint. Next time you come across an example like this, treat it with extra skepticism.