A Bookstore in Ruby

Nathan JohnsonNathan Johnson
12 min read

Introduction

This post is about a study strategy and how that strategy relates to a specific course within the Core Curriculum of Launch School. The course in question is Object Oriented Programming (OOP), denoted in the Ruby track as RB120 (lessons) and RB129 (assessments).

This post is the product of a collaboration with fellow student Wook Kim, and its backstory begins with an insight he had after completing his RB129 assessments, especially the interview.

Wook felt something was missing in his preparation that would have solidified his understanding and fluency, but he had an idea of what would have helped. He eagerly described his proposal and urged me to try it as I prepared for the same assessments.

Having passed both assessments after putting into practice Wook's idea, I can strongly recommend it.

Wook's Insight

Wook described two struggles.

First, he felt that while he understood individual concepts, he didn't have a comprehensive mental model to tie everything together.

Second, when asked to provide examples, he had to come up with them on the fly. His suggested study strategy would address both points.

The technique he proposed was to pick a real world domain I was familiar with and model it in Ruby using the OOP concepts from the course. Specifically, his idea was to create a single model covering all the concepts in one place. I'll call this the "unified model."

I will attempt to detail a "recipe" for this strategy, but I improvised every step of the way. It's plausible that I didn't make every best choice.

Furthermore, when I went back to Wook partway through my studies to clarify the specifications for an end product, he instead encouraged me to focus on "the why."

The point is not to write a Ruby file fulfilling a certain specification; the point is an integrated understanding of OOP concepts. If the path you take creates understanding, mission accomplished.

"Part of a Balanced Breakfast"

This was far from the only strategy I employed while studying for these assessments.

I can highly recommend this technique together with peer study sessions, TA sessions, and good old-fashioned curriculum review.

I err on the side of over-preparing for Launch School assessments. These were no exception, but they were my most comfortable assessment experiences yet. The unified model strategy was key in bringing me from tentative understanding to fluency and quick recall.

The Recipe

Think of my detailed instructions as more a record of my improvisation than dogma you must follow.

  1. Domain: Choose something familiar. Workplaces, sports teams, and other human organizations are great candidates. Pick something where a general audience will be able to understand your snippets without extra context.
  2. Individual snippets: In the context of your chosen domain, give a code example to illustrate XYZ concept. Can you model the positions on a team using class inheritance? How does encapsulation relate to a customer's experience in a restaurant? What kind of collaborator objects would a PlayerCharacter class have in a roleplaying game?
  3. Refine: Make your snippets better! I elaborate on this later. With many of these concepts, we are in the fuzzy zone of making design decisions with tradeoffs. Try things out!
  4. Unify: Start with a checklist of the concepts you want to make sure your unified model illustrates. Then do it all in one big (hopefully not too big!) Ruby file. Refine further!
  5. Practice: With little to no reference to your existing snippets, quiz yourself by writing from scratch code illustrations of different concepts from the course.

Why Metaphor? / What It Is We're Doing Here

The Value of the Concrete

We deliberately use nontechnical nouns and verbs as the subjects of our metaphors when developing mental models.

The Launch School curriculum frequently illustrates object oriented concepts through examples such as types of vehicles or animals.

These concrete things make for much better illustrations than abstract data types because we can jump-start our understanding of code relationships by piggybacking on our existing understanding of relationships in the real world.

Piggyback on Existing Integration

Setting your illustrations of different OOP concepts in the same domain makes them easier to integrate.

Your "assignment" is not just to write code so much as to develop a library of connected metaphors to fluently draw from when called upon to explain concepts. We develop the metaphor to integrate our mental models, and we write corresponding Ruby to develop integrated examples.

Finally, in case it is not already clear, we are not writing a program here. These are more like mental maps or diagrams in code form.

Putting It Into Practice

When I first began reviewing OOP concepts, the prospect of illustrating them all in a unified model was daunting. Before writing the unified model, I alternated reviewing course materials with writing individual snippets to illustrate specific concepts.

I started with the concepts I was most confident with. Class inheritance and module mixins?--no problem. Constant scope?--let me get back to you. This brings me to a clarifying question:

Q: When should I start doing this?
A: Once you have completed the lessons and seen the study guide, you may be comfortable enough with some concepts to write illustrative examples of them. Later, combine concepts into larger illustrations, and eventually a unified model. In other words, this is a review strategy. Do it when you're reviewing!

My Particulars

For a domain I picked a bookstore, and began by writing individual snippets to illustrate the concepts.

Attempting to write the snippets revealed shortcomings in my understanding that I addressed mostly through rereading the course material. Refining the snippets brought fluency and clarity. I wrote some intermediate snippets that combined several concepts, and eventually wrote the suggested unified model that utilized all the key concepts from the course.

Coming up with your own code illustrations and model is the helpful bit, so I won't be providing my final unified model for your reference. However, I will give smaller examples, both to paint a broad picture of what I did and to illustrate some of the lessons I learned in revising.

The Bookstore Metaphor

So, how did I model object oriented programming concepts in the domain of a bookstore?

  • Class inheritance: types of employees (manager, bookseller, owner...)
  • Polymorphism: unrelated product types that all get treated the same at the cash register
  • Collaborator objects: an instance of Inventory stores instances of Book
  • Protected methods: Employee instances can access certain methods such as a work schedule from other employees, but those methods are off limits to a Customer

Hopefully you get the idea.

Creating Effective Illustrations

I revised many of my snippets. Some improvements were immediately obvious, but others came after going to TA sessions and SPOT sessions (Launch School's peer-lead study groups) and getting feedback on the illustrations I provided.

I'll demonstrate some rules of thumb that may improve your illustrations.

Simplify

You may feel the urge to write code examples that are more fully featured, that look more like a "real program." This impulse can add unnecessary and distracting detail.

Here I'll share two snippets: one that has some unnecessary complexity and then a simplified version.

I wrote the following to illustrate collaborator objects and private getters:

class Inventory
  def initialize
    @stock = Hash.new(0)
  end

  def [](book)
    stock[book]
  end

  def []=(book, quantity)
    stock[book] = quantity
  end

  def all_titles
    puts stock.keys.map(&:title)
  end

  private

  attr_reader :stock
end

class Book
  attr_reader :title

  def initialize(title)
    @title = title
  end
end

white_fang = Book.new('White Fang')
piranesi = Book.new('Piranesi')

store = Inventory.new
p store[white_fang] # 0
store.all_titles # no output

store[piranesi] = 3
store[piranesi] -= 1
p store[piranesi] # 2
store.all_titles # Piranesi

Upon further reflection, I realized that the above snippet can be cleaned up a bit. Using a hash introduces complexity that doesn't further the concept I'm trying to illustrate. Also, instantiating two book objects serves no purpose here; one would do just fine.

Below is a slightly simplified snippet. It uses the simpler array structure. Also, the #in_stock? method is I think a more straightforward example (than #all_titles) of a public method that responds based on private state that is not exposed.

class Inventory
 def initialize
   @stock = []
 end

 def in_stock?(title)
   stock.any? { |book| book.title == title }
 end

 def receive(book)
   stock << book
 end

 private

 attr_reader :stock
end

class Book
 attr_reader :title

 def initialize(title)
   @title = title
 end
end

inventory = Inventory.new
p inventory.in_stock?('Piranesi') # false
inventory.receive(Book.new('Piranesi'))
p inventory.in_stock?('Piranesi') # true
inventory.stock # error

Every bit of simplicity counts. When you are taking an assessment, half of your brain might be in panic mode. You want to build up a library of examples that you won't get tripped up trying to reproduce. Also, when you eventually unify your snippets, compact and efficient examples will be easier to integrate.

To simplify a snippet, ask yourself questions like:

  • Can I illustrate this with fewer methods?
  • Does this collection need this many entries? (Your example array probably doesn't need ten elements. Three will do.)
  • Can I use a simpler data structure?
  • When I "run some code" at the end to demonstrate the concept, are my examples redundant? What's the simplest way to show how it works?

Regarding that last point...

Run Some Code

Don't just write classes that illustrate the concepts at hand; run some code to show how it works!

The above snippets are primarily concerned with illustrating collaborator objects. As such, it's critical to include that last section where I populate the Inventory with a Book object and test the public interface. In some cases it may be useful to demonstrate code that throws an error, for instance to show where we can and can't invoke private methods.

When putting together your unified model, I recommend leaving out the demonstrative code-running. It clutters the file to demonstrate dozens of things.

Annotate Your Snippets

As your understanding improves and you review snippets you've written, you may come to realize... well, all sorts of things! Leave comments in your snippet files to preserve your observations alongside the relevant code.

One genre of comment I left in several snippets was essentially, "When would I want to use this?"

For instance:

# class variables are useful for storing common state
# ...tied to the class rather than specific instances
# a basic example is incrementing the instance count on initialization

class Book
  @@books_on_record = 0

  def self.how_many_exist
    @@books_on_record
  end

  def initialize
    @@books_on_record += 1
  end
end

Some things to consider reflecting on and annotating:

  • When would I use this concept? What is a classic use case?
  • Does the real world situation I'm modeling here feel like it would be better modeled with a different structure? (When illustrating polymorphism, you may come to realize the nouns you are using to illustrate duck typing would make more sense in a common class inheritance structure, or vice versa.)

Your snippets may be saved in Ruby files, but they are part of your notes, and you can write whatever you want in them!

Contradict Yourself

I've shared a couple implementations of a Book class that looked quite different. You may wonder if your snippets should play nice with each other and be non-contradictory, so that you can easily just combine them when you want to build a unified model. I say no!

When you are writing many snippets to illustrate a particular concept or a few concepts together, this is a great time to "explore the space" of your metaphor. Model the same noun in different ways, or use different nouns to model the same concept, and see what looks better.

The Book snippets I've shared are not contradictory in the sense that you could combine them, but in each snippet I only included the detail necessary for the concept at hand. The same overall principle applies in that each snippet is an independent effort to illustrate something. Consistency comes later.

The Unified Model

All this talk about snippets, but wasn't the whole point to build a unified model? When should you put it all together and what should it look like?

Let's refresh what we're trying to do here:

  • We want to integrate our understanding of different concepts.
  • We want fluency with giving examples to illustrate concepts.

And our strategy is:

  • We develop an extensive mental model mapping object oriented programming concepts to a real world domain.
  • We use that metaphor as the setting for code illustrations of concepts.

Truth be told, writing separate snippets that draw from the same context does a lot of work solidifying our understanding. You could stop there and have benefited greatly, but don't! Distilling that work into a unified model is quite worthwhile.

Why Bother With the Unified Model?

Goal Power

One reason to plan on creating the unified model is simply the purpose it serves being in the back of your mind as a goal. It is a daunting task that acts as a litmus test for your confidence in the material, and it encourages taking intermediate steps of study to clarify and condense your understanding.

Resolving Contradictions

Putting your examples for all the concepts in the same place encourages compact examples, but it also forces you to resolve any contradictory or confusing overlapping metaphors.

I encouraged experimentation within individual snippets, but when it comes to the unified model you need to "make a choice" about which nouns to tie to which concepts.

This is the final stage of refining the examples you will draw from when asked to explain concepts. You want to be able to illustrate all concepts without your examples stepping on each other's toes.

An Aside: Be Flexible

The particular implementation you use to illustrate a concept in your unified model is not the only way to do things. You may very well do things differently on an assessment.

You will be more flexible if you have tried out similar implementations during the snippet phase.

Quick Reference

The desire for tidy examples brings me to the final use of the unified model: a quick reference study tool. If all the relevant concepts are illustrated in your unified model, you can skim it and ask yourself "do I understand what's happening here?" and rest assured you're covering everything. I'm talking about jogging your memory, not achieving initial mastery.

What It Is, What It Isn't

To reiterate what I'm suggesting the unified model be:

  • The unified model's purpose is more like notes than a program.
  • The unified model illustrates all relevant concepts.
  • All illustrations in the unified model should be set in the same general context.
  • Different aspects of the unified model do not need to interact with each other in code.

Closing Thoughts

Good Enough

When I created my "unified" model, the disconnectedness of different illustrations made me feel sort of like I had just put separate snippets in the same file. That's fine!

Having that file allowed me to go to one place to review the model I developed, and ensured that the particular illustrations I drilled late in the study process were not contradictory.

By that time I was starting to think "I should just take the assessment already," so I wasn't overly concerned with spending a long time doing it just right.

This goes back to my point about how having the unified model as a goal leads you to some great review before you even get to the "unified" part.

"They're More Like Guidelines"

Again, my detailed instructions are an attempt to be clear and helpful, rather than prescriptive.

The initial suggestion I followed left room for interpretation. What I improvised worked well for me and I wanted to share it. Take what makes sense to you and adapt it to your needs!

Reach Out (I'll Be There)

I'm very curious to hear from other students about whether they've done something similar and how it went. Feel free to reach out with your thoughts.

1
Subscribe to my newsletter

Read articles from Nathan Johnson directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nathan Johnson
Nathan Johnson