A model is not a simulation

by Jason Swett,

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 sing and dance methods. Another simulation.

The next was an Animal class with move and 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 dog.bark and car.repaint. Next time you come across an example like this, treat it with extra skepticism.

2 thoughts on “A model is not a simulation

  1. Jamie

    Honestly, I find the hangman example more confusing than the more common examples using animals or vehicles.

    Why is that better OOP? Because it models rather than simulates? It’s hard for me to follow the argument.

    What about a card game library such as Poker. Wouldn’t those be described as simulations?

    deck.deal
    card.value
    hand.royal_flush?

    Reply
    1. Jason Swett Post author

      Interesting, I’m surprised. Yeah, I’d say better OOP because it’s trying to model a concept, not trying to be a simulation (which is an impossible/nonsensical attempt anyway, e.g. dog.bark).

      A card game with methods/attributes like card.value and hand.royal_flush? certainly wouldn’t be a simulation AFAICT. The key thing is that something real happens. When you do hand.royal_flush?, presumably it would check all the cards in your hand and see if they comprise a royal flush. When you do card.value, presumably it would tell you if it’s an ace of hearts or jack of spades or whatever. Examples like dog.bark and car.repaint don’t pass that test because there’s no conceivable way that they could actually work.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *