Date: September 27, 2025
This blog post consists in two parts:
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:
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:
attr_reader and initializerClasses 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.lyricsKnowing 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:
We extracted that method verse out into a separate class called BottleVerse
Then we injected that back in.
We opened the door to have many different classes that play the verse_template role, as long as they conform the lyrics API
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
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
verse_template holds, will respond to newnew accepts an argumentnew responds to lirycsdef 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:
BottleNumbernumber is turning it into something elsedef 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.
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.
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
lyricsof the “99 Bottles” song out ofBottlesand put them into a newBottleVerseclass. It then injected an instance ofBottleVerseback intoBottles. ExtractingBottleVersereduced the Bottles’s responsibilities, making it easier to understand and maintain. InjectingBottleVerseintoBottlesloosened the coupling betweenBottlesand the outside world.Bottlesnow 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 tolyrics(number).
The impetus behind extracting
BottleVersewas a new requirement to produce songs with different lyrics, that is, to vary theverse. The recent refactorings satisfied that requirement by following a fundamental strategy of object-oriented design: extracting theBottleVerseclass 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:
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??”.