dom lizarraga

dominiclizarraga@hotmail.com

POOD Session 8: Vary Verse & Honor Demeter, Isolate Object Construction

33 minutes to read

Session 8: Vary Verse

Date: September 27, 2025

This blog post consists in two parts:

Key Concepts

  • Law of demeter
  • Composition

Vary Verse

Watch 1: Appreciating the Mechanical Process

This block introduces a new requirement to vary the lyrics of the verse which allows us to begin considering whether and when you might voluntarily make changes to improve code.

In this block introduce the ideas of ‘pseudocode’ and ‘coding by wishful thinking.’ This block culminates with the Dependency Inversion Principle (DIP), one of the most broadly useful concepts in object-oriented programming.

The current code is simple because we started out with no abstractions. No incorrect abstractions or make abstractions from things we didn’t really understand.

Then we used repeatable programming techniques to follow code smells, then extract differences and isolate variance

The story this code tells is that there’s a more abstract kind of verse, that allthe 99 bottles verses have in common, a single abstraction.

def verse(number)
  # here the number (Integer) gets transformed into a BottleNumber instance
  # then goes into the Factory
  bottle_number = BottleNumber.for(number)
  # here is the template
  "#{bottle_number} of beer on the wall, ".capitalize +
  "#{bottle_number} of beer.\n" +
  "#{bottle_number.action}, " +
  "#{bottle_number.successor} of beer on the wall.\n"
end

If you were to describe the responsibility of verse method, you would say it converts a number into a BottleNumber, and then it uses that BottleNumber to create a verse.

The use of the word “and” in that sentence, converts a number into a BottleNumber AND uses that BottleNumber to make a verse that word “and” should give you pause.

Given “and” shows that verse method have more than one responsibility.

These 6 lines of code, represent not much hurt, it can be considered as a code smell but since we have not gotten any other requirement we can leave it as it’s.

However, if you voluntarily want to refactor this code into something more SOLID, consider the outlook that you’re the steward of someone else money and if this change in the present will save money in the future or maybe you’re better off and wait the next requirement.

What you want is to have a set of internal rules that allow you to make bets on whether or not changes are going to turn out, and you want those bets to pay off more often than not.

Code smell here is “Speculative generality” making improvements to code based on your ideas that will pay off later.

Really good progammers, write really changeable code.

We’ll see the heuristics, the set of rules can make you more confident, not that every one of these changes is going to save you money later, but they probably won’t cost you money, and enough of them will actually save you money so that it’s worth doing it all the time.

Experienced programmers are like little artificial intelligence machines that have been trained on many, many, many examples of code. Their experience of making judgments, and looking at how those judgments turned out over years, and years, and years, build up into a kind of intuition. And you can think of that programming intuition really as just a form of pattern matching.

They can look at a set of code and recognize shapes that are going to cause trouble later, and they know how to transform those shapes that are going to be a problem into shapes that are going to be easy to change later.

Watch 2: Clarifying Responsibilities with Pseudocode

New requirement is to make code produce different song verses.

As we use the .downto() method we must ask if the new verse will count downwards too, so that we can know we need to change code or not.

If we need to know if code is Open or Closed to change we can refer to diagram:

code_is_open_diagram

If code is not open and we don’t know how to make it open, we look for code smells.

The benefits of writing bad code is that we introduce code smells that will give us a glimpse of the future that we can preemptively decide what code smells we are going to have and use refactoring to remove them.

Here, is a first sketch with pseudo code:

  def verse(number)
    # if 99_bottle_song
    bottle_number = BottleNumber.for(number)

    "#{bottle_number} of beer on the wall, ".capitalize +
    "#{bottle_number} of beer.\n" +
    "#{bottle_number.action}, " +
    "#{bottle_number.successor} of beer on the wall.\n"
    #
    # elsif unknown_song_2_verse
    # ...
    #   assemble verse for unknown song 2
    # ...
    # elsif unknown_song_3
    # ...
    #   assemble verse for unknown song 3
    # ...
    # end
  end

Above if statement is just going to grow, and grow, and grow, and grow, and grow, until your life is a disaster so pseudo code help us see if we are heading a dead end road and identify it quickly.

Solution: the problem is, we have this conditional with a number of branches, and each of those branches should represent an object, a different kind of verse variant. And that means we should apply the Extract Class refactoring to pull those different verse_templates out into some other class.

Watch 3: Extracting the Verse

As we did back in chapter 5, when we had the code smell of “primitive obsession” on number argument, and we created BottleNumber we need to follow the next steps:

  1. Choose a name for the new class
  2. Add attr_reader and initializer
  3. Copy methods from old to new (not cut!)
  4. Wire new class into old (NewClass.new)
  5. Clean up extra args

Classes should be name after what they are !

In this case BottleVerse:

class Bottles
  def verse(number)
    BottleVerse.new(number).verse(number)
    # bottle_number = BottleNumber.for(number)

    # "#{bottle_number} of beer on the wall, ".capitalize +
    # "#{bottle_number} of beer.\n" +
    # "#{bottle_number.action}, " +
    # "#{bottle_number.successor} of beer on the wall.\n"
  end
end

class BottleVerse
  attr_reader :number

  def initialize(number)
    @number = number
  end

  def verse(number)
    bottle_number = BottleNumber.for(number)

    "#{bottle_number} of beer on the wall, ".capitalize +
    "#{bottle_number} of beer.\n" +
    "#{bottle_number.action}, " +
    "#{bottle_number.successor} of beer on the wall.\n"
  end
end

Up to this point and running after each change tests, we were able to create a new class, make it syntatically correct, invoke it from the old class and now we still need to remove old-extra-arguments.

Watch 4: Coding by Wishful Thinking

In object-oriented programming, we think of design as happening from the message sender’s point of view.

AI example code of above phrase:

# Design from the message sender's point of view
#
# Don't ask: "What CAN this object do?" (receiver perspective)
# Ask instead: "What do I NEED this object to do?" (sender perspective)
#
# The sender (trip) only cares about getting a description, it doesn't 
# care whether the vehicle is a Car, Bike, or Plane. We design the 
# interface based on what the sender needs to send, not what the 
# receiver happens to have.

class Trip
  def initialize(vehicle)
    @vehicle = vehicle
  end

  def plan
    puts "Planning trip using: #{@vehicle.describe}"
  end
end

# All vehicles respond to the message the sender needs
class Car
  def describe
    "a comfortable car"
  end
end

class Bike
  def describe
    "an eco-friendly bike"
  end
end

# Usage - Trip sends 'describe', vehicles respond
trip1 = Trip.new(Car.new)
trip1.plan  # => Planning trip using: a comfortable car

trip2 = Trip.new(Bike.new)
trip2.plan  # => Planning trip using: an eco-friendly bike

The key: Trip (the sender) defines what message it needs (describe), and all receivers must respond to that interface.

Here is the result of wishful thinking:

class Bottles
  def verse(number)
    BottleVerse.new(number).lyrics
  end
end

class BottleVerse
  attr_reader :number

  def initialize(number)
    @number = number
  end

  def lyrics
    bottle_number = BottleNumber.for(number)

    "#{bottle_number} of beer on the wall, ".capitalize +
    "#{bottle_number} of beer.\n" +
    "#{bottle_number.action}, " +
    "#{bottle_number.successor} of beer on the wall.\n"
  end
end

Watch 5: Inverting Dependencies

We just created a BottleVerse class because we’re trying to fulfill a new requirement to produce new songs that are like 99 Bottles, but have different lyrics.

In the prior section, we extracted the thing we wanted to vary, the verse method, those lyrics out into a new BottleVerse class and yet we still can’t actually fulfill the requirement.

Dependency Inversion, says that you should depend on abstractions, not concretions. And right now, here in this code, the name of the BottleVerse class is a concretion. (This create a tight coupling between these two classes)

class Bottles
  def verse(number)
    BottleVerse.new(number).lyrics # This makes both classes to be tight together
  end
end

Depending on the class name BottleVerse is bad but not as bad as having Bottle unable to interact with other classes to produce .lyrics.

What we want to do here is change this code such that the Bottles class can interact with any other object that can supply lyrics. (we need to loosen the coupling between Bottles and BottleVerse)

At this point Bottles know two things:

  • BottleVerse which is the concretion
  • The abstraction that is .lyrics

Knowing about .lyrics is ok, Bottles somehow gotta know what message send, but the object receiver is the one that needs fixing.

If an object can play a common role, that role should have a name, we can call it verse_template this will replace the concrete dependency BottleVerse

Here, we have extracted BottleVerse class and injected of a constant

class Bottles
  attr_reader :verse_template

  def initialize(verse_template: BottleVerse)
    @verse_template = verse_template
  end

  def verse(number)
    verse_template.new(number).lyrics
  end
end

Now we have the option of creating other players of this role and producing varying songs.

This idea of extracting variance into individual classes of their own, and then choosing the right variant and injecting it back into the class at runtime, is known as composition.

Here is a Sandi drawing to illustrate this change:

Bottles used to have a verse method:

code_is_open_diagram

We extracted that method verse out into a separate class called BottleVerse

code_is_open_diagram

Then we injected that back in.

code_is_open_diagram

We opened the door to have many different classes that play the verse_template role, as long as they conform the lyrics API

code_is_open_diagram

A much shorter way to say this is that we just isolated the code we wanted to vary.

It’s one of the core concepts of how to write good OO that you take things that you want to vary, and instead of wrapping them in conditionals to provide variation, you take what would, be branches of a conditional and pull them out into classes of their own, and then someone, somewhere will pick the right object and inject it into the class that needs a variant. The principle is named Dependency Inversion.

Here’s the simplest explanation I can give: Depend on abstractions, not concretions.

Before the name of the BottleVerse class was a concretion. We didn’t want to depend on that, instead, we wanted to invert that dependency so that we were depending on the abstraction, which is the idea that we wanted a verse_template rather than the concretion, the actual class name.

When we depend on abstractions, it means that we can get other players of that abstract role and use them instead of the concretion.

class Bottles
  attr_reader :verse_template

  def initialize(verse_template: BottleVerse)
    @verse_template = verse_template
  end

  def verse(number)
      verse_template.new(number).lyrics
  end
end

class BottleVerse
  attr_reader :number

  def initialize(number)
    @number = number
  end

    def lyrics
    bottle_number = BottleNumber.for(number)

    "#{bottle_number} of beer on the wall, ".capitalize +
    "#{bottle_number} of beer.\n" +
    "#{bottle_number.action}, " +
    "#{bottle_number.successor} of beer on the wall.\n"
  end
end

class RandomVerse
  def initialize(number)
    @number = number
  end

  def lyrics
    "#{@number} random things on the shelf..."
  end
end

class SodaVerse
  def initialize(number)
    @number = number
  end

  def lyrics
    "#{@number} bottles of soda pop..."
  end
end

Honor Demeter, Isolate Object Construction

Watch 1: Obeying the Law of Demeter

Law of Demeter has something to do with lines of code that have more than one dot.

The next piece of code seems inocent but has multiple dependencies

  • Whatever verse_template holds, will respond to new
  • new accepts an argument
  • and the thing returned by new responds to lirycs
def verse(number)
  verse_template.new(number).lyrics
end

Every one of these things is a dependency, and a dependency is just something that you know about, that someone else is in charge of, so that if they changed it, you might have to change in turn.

Dependencies are unavoidable, objects have to know about one another in order to get anything done but we’d like to have loose coupling between objects.

We’d like to have every object have the fewest number of absolutely necessary dependencies so that we don’t end up having lots of distant and unrelated changes break our current code.

The Law of Demeter says: “You should not talk to your friends friends.”

Theline of code above contains a message chain where each message along the chain returns a “different kind of object”. Each of the returned objects conforms to a different API. That’s an important component of the Law of Demeter.

If this message chain had returned a series of objects, all of which conform to the same API, this wouldn’t be a violation. If it was a bunch of Strings, or a bunch of Arrays, we don’t really care. What we’re concerned about is where, along the chain, every message that you send returns an object that’s different than the other ones.

Here, is an example that violates Law of Demeter:

class Foo
  def durability_of_preferred_toy_of_best_friends_pet
    best_friend.pet.preferred_toy.durability
  end
end

Above code Foo has to know that it has a best_friend, it has to know that best_friend has a pet, has to know that pet has a toys, and toy has durability.

It might not seem so terrible when you look at the line of code, but think about the problem with testing this.

Real objects have slow running operations, then your tests are going to be slow, unless you break in and stub them or stub in stubs or stub in stubbed stubs.

When you see that you have to stub in stubs in order to test an object, it’s because you have violated the Law of Demeter.

How can you fix this? Well, the time honored method for curing demeter violations is through message forwarding. And all that means is you go into each intermediate object and write a little helper method to basically remove that hop.

class Toy
  def durability
    1.hour
  end
end

class Pet
  def durability_of_preferred_toy
    preferred_toy.durability
  end
end

class Friend
  def durability_of_preferred_toy_of_pet
    pet.durability_of_preferred_toy
  end
end

class Foo
  def durability_of_preferred_toy_of_best_friends_pet
    best_friend.durability_of_preferred_toy_of_pet
  end
end

With these changes Foo talk directly to its collaborators. And what that means is, if you want to stub out the behavior of Friend, you can do it by creating one stub. You don’t have to stub in stub in stub in stubs in your tests.

So it no longer is related to that network of objects, it just have one dependency on one direct collaborator.

There’s one more improvement we can implement, the method .durability_of_preferred_toy_of_pet and so we haven’t really freed ourselves from the structure of the object graph, the object dependency graph.

We need to ask “What does Foo really want?”

Messaging forwarding helped with testing but it doesn’t help with identifying concepts in your domain.

Why does Foo want to know durability of a toy of its best friend pet?

Imagine it’s because Foo is trying to arrange a play date, and it wants to know how long the pets can play before they tear up all their toys.

class FriendWithPet
  def playdate_time_limit
    pet.durability_of_preferred_toy
  end
end

class FriendWithChild
  def playdate_time_limit
    child.tolerance_of_social_contact
  end
end

class Foo
  def playdate_time_limit
    best_friend.playdate_time_limit
  end
end

Since we’re injecting some kind of a Friend object into Foo, they both work. They’re interchangeable from Foo’s point of view. Because we’re using dependency injection, and because we have identified what Foo wants, now we can collaborate with a whole universe of friends that have different constraints on playdate_time_limit.

And that’s the value of fixing demeter violations. They force you to step back from the current structure of your code and to create messages that embody what the sending object wants.

Watch 2: Identifying What the Verse Method Wants

Now that we’ve been introduced to Law of Demeter, it’s time to figure out what verse method wants.

The rule about injecting dependencies is that you want to inject the thing that you plan to talk directly to, which suggests that in most cases you would inject an instance of a class.

If testing is hard, it suggests that parts of your application are going to be difficult to reuse in other contexts.

See below a proposal for sending .lyrics to the verse_template and extracting the .new from Bottle out to BottleVerse.

class Bottle
  def verse(number)
    # verse_template.new(number).lyrics
    verse_template.lyrics
  end
end

class BottleVerse
  def self.lyrics(number)
    new(number).lyrics
  end

  attr_reader :number

  def initialize(number)
    @number = number
  end

  def lyrics
    bottle_number = BottleNumber.for(number)

    "#{bottle_number} of beer on the wall, ".capitalize +
    "#{bottle_number} of beer.\n" +
    "#{bottle_number.action}, " +
    "#{bottle_number.successor} of beer on the wall.\n"
  end
end

Watch 3: Pushing Object Creation to the Edge

The last issue we have with current code is the following:

  • it contains hardcoded reference to BottleNumber
  • it has a blank line in it (it likely indicates a change of topic, failure of single responsability)
  • the only thing that does to number is turning it into something else
def lyrics
  bottle_number = BottleNumber.for(number)

  "#{bottle_number} of beer on the wall, ".capitalize +
  "#{bottle_number} of beer.\n" +
  "#{bottle_number.action}, " +
  "#{bottle_number.successor} of beer on the wall.\n"
end

Well-designed object-oriented applications consist of loosely-coupled objects that use polymorphism to vary behavior.

Loose coupling happens with dependency injection; we choose and inject objects to have behavior.

Polymorphism means that I can have a bunch of different kinds of objects that I inject that conform to the same API but provide different implementations.

Once you define an API and start creating objects that play roles and injecting those objects to get variants, the whole world opens up where you can easily create new variants without changing existing code.

A couple of rules to make code in the present more flexible to unknown change in the future.

  1. I want to resist allowing methods to know the names of concrete classes. It’s a bad idea. Your code would be more flexible and changeable in the future if you didn’t do it.

Always look for an opportunity to get those concrete constant names out of my methods. And that’s just a general rule that applies in every case.

  1. The next rule, is always look for opportunities to separate the construction of objects from the use of those objects

Push the transformation of other data into objects that we’re using away from the code in the center of the app, and try to push it back up the stack into the earliest possible place I could change it.

class BottleVerse
  def self.lyrics(number)
    new(BottleNumber.for(number)).lyrics
  end

  atrr_reader :bottle_number

  def initialize(bottle_number)
    @bottle_number = bottle_number
  end

  def lyrics
    "#{bottle_number} of beer on the wall, ".capitalize +
    "#{bottle_number} of beer.\n" +
    "#{bottle_number.action}, " +
    "#{bottle_number.successor} of beer on the wall.\n"
  end
end

Sandi’s summary of this chapter:

This block pulled the lyrics of the “99 Bottles” song out of Bottles and put them into a new BottleVerse class. It then injected an instance of BottleVerse back into Bottles. Extracting BottleVerse reduced the Bottles’s responsibilities, making it easier to understand and maintain. Injecting BottleVerse into Bottles loosened the coupling between Bottles and the outside world. Bottles now thinks of itself as being injected with players of the verse template role and will happily collaborate with any newly arriving object as long as that object responds to lyrics(number).

The impetus behind extracting BottleVerse was a new requirement to produce songs with different lyrics, that is, to vary the verse. The recent refactorings satisfied that requirement by following a fundamental strategy of object-oriented design: extracting the BottleVerse class isolated the behavior that the new requirement needed to vary.

While continuing to lean on code smells and refactoring recipes, this block introduced the idea of a programming aesthetic. A programming aesthetic is the set of internal heuristics that guide your behavior in times of uncertainty. Vague feelings about the rightness of code become part of your aesthetic once you can eloquently and convincingly use actual words to explain your concerns and proposed improvements. A good programming aesthetic focuses attention on improvements that are likely to prove worthwhile.

This block suggested five precepts that belong in everyone’s object-oriented programming aesthetic:

  • Put domain behavior on instances.
  • Be averse to allowing instance methods to know the names of constants.
  • Seek to depend on injected abstractions rather than hard-coded concretions.
  • Push object creation to the edges, expecting objects to be created in one place and used in another.
  • Avoid Demeter violations, using the temptation to create them as a spur to search for deeper abstractions.

The practical effect of following these precepts is to loosen the coupling between objects. The code to which you would apply them generally already works so adherence might seem optional, and is certainly not free. Complying with these precepts will frequently increase the amount of code and add levels of indirection, at least in the short term. However, these added costs are overwhelmingly offset by the eventual savings accrued as a result of decoupling.

Any application that survives will change. The only thing of which you can be more confident is that you cannot predict where this change will occur. The certainty of change coupled with the uncertainty of that change’s location means that your best programming strategy is to strive to loosen the coupling of all code everywhere from the moment of initial creation.

Therefore, these precepts don’t attempt to guess the future; rather, they leverage against it. Instead of writing code that speculatively imagines a later need for one specific feature, they tell you to loosen the coupling of all code so that you can easily adapt to whatever future arrives.

Uncertainty about the future is not a license to guess; it’s a directive to decouple. Your future will be brighter if you develop a programming aesthetic that drives you to do so.

Short quiz

Define the Law of Demeter. I know this can be awkward, but try your best. :-)

You can talk to your friends, but not to their friends. More technically, you should only send messages to your direct dependencies. You should not reach across your dependencies to talk to their dependencies.

If you violate Demeter by talking to your friends’ friends, you tightly couple yourself to a network of objects. This makes your application fragile, that is, your object might break because of an unexpected change to a distant and apparently unrelated thing.

You can avoid Demeter violations by designing code based on what an object “answer??” rather than what other objects “answer??”.

  • wants, already do