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