Practical Object Oriented Design in Ruby

This is one of the simplest, most elegant, and easy to understand book I’ve read so far on object-oriented design with concise examples.

Sandi Metz, the author of this book has been writing software for 30+ years and had amazing talks on OO design.

The book examples are focused on Ruby, a dynamically typed language. After reading the ruby examples, Coming from statically typed languages like Java, Kotlin and Dart, I can say that the language does not matter much. The concept, ideas, and design decisions explained through examples are the same across any object-oriented language.

On another side, I don’t know when I will get a chance to work on ruby and this was a good opportunity for me to learn about other languages. So it’s a win-win situation for me.

Following are a few takeaways for me from this book.

What is Object-oriented design?

The author says “Object-oriented design (OOD) requires that you shift from thinking of the world as a collection of predefined procedures to modeling the world as a series of messages that pass between objects.”

OO design is nothing but how one object communicates with other objects via message passing.

Shifting my thinking from classes and functions to message passing enabled me to have a better design by seeing code responsibilities more clearly.

Questions like, which code belongs to which object, what responsibilities it should have, and what it should delegate to other objects become more clear to me.

Why do we need Object-oriented design?

Change is the only constant. Business changes, team changes, framework, language, environment changes.

Building software keeping OO design in mind helps us to cope with change easily. It also helps us to estimate how much a change will cost in terms of time.

OO design won’t have much benefit if your software does not change over time. For example, a static website. But in the real world, that’s hardly the case.

Single Responsibility

Most of the time in OO design is spent on naming things and deciding which code belongs where.

Single responsibility is the first design principle of SOLID. The book has done a great job of explaining how we can design classes with a single responsibility thinking object as message passing.

My learning so far about single responsibility is that we cannot learn it by just reading a book or looking at the code or watching videos. We have to practice it first. The more we practice, the easier it gets.

Managing Dependencies

For passing messages between objects, one object must depend on the other. And that is how we start to create dependencies.

Object-oriented design is about managing dependencies. It is a set of coding techniques that arrange dependencies such that objects can tolerate change.

Breaking dependencies in code is not easy. The book has enough examples of different tips and tricks for separating dependencies using techniques like constructor injection(DI), duck types (Interface), and when to use Inheritance vs compositions.

Get Enough Information

Most of the time when we see a duplicate code, we have a temptation to abstract it right away.

Reading this book from start to end I realized that OO design is an exploration journey. We don’t have all the information upfront to make a design decision.

We make design decisions as we get enough information about the abstraction or duplicated code.

If you see a duplicate code but don’t have enough information about it then it is better to not abstract it. Because A design that does the wrong thing might produce great metrics but may still be costly to change.

This happens to me all the time. So as rule I extract the duplicate code when it’s repeated three times rather than two.

Cost-effective Testing

I wish the book had covered more on Testing. A good design allows us to do testing effectively. Having single responsibilities and segregations helps us with testing. The more complicated the design, the harder it is to test.

If you find anything hard to test then probably that class is doing a lot of things or has many dependencies.

Since Ruby is a dynamic language. The book gives an example of how can we test interface, abstractions, and inheritance without breaking the production code. But I think this won’t be necessary if you are working on statically types language (the compiler does the job for you).

You can get this book from amazon.

If you like this kind of content, you can subscribe to my blog and get notified first. You can also follow me on Twitter or Email me.

Highlighted Quotes

  1. The problem with poorly designed small applications is that if they are successful, they grow up to be poorly designed big applications.
  2. When a project misses its delivery deadline, even if this happened because of changes to the specification, the programmers are at fault. If, however, it is delivered on time but doesn’t fulfils the actual need, the specification must have been wrong, so the customer gets the blame.
  3. Design relies on your ability to translate theory into practice.
  4. Design is more the art of preserving changeability than it is the act of achieving perfection.
  5. When the future cost of doing nothing is the same as the current cost, postpone the decision. Make the decision only when you must with the information you have at that time.
  6. This “improve it now” versus “improve it later” tension always exists. Applications are never perfectly designed. Every choice has a price. A good designer understands this tension and minimizes costs by making informed tradeoffs between the needs of the present and the possibilities of the future.
  7. An object whose purpose is to create other objects is a factory;
  8. Attempting to write statically typed code in a dynamically typed language yields code that suffers from the worst of both worlds.
  9. Inheritance is, at its core, a mechanism for automatic message delegation. It defines a forwarding path for not-understood messages.
  10. Because no design technique is free, creating the most cost-effective application requires making informed tradeoffs between the relative costs and likely benefits of alternatives.
  11. Composition is the act of combining distinct parts into a complex whole such that the whole becomes more than the sum of its parts. Music, for example, is composed.
  12. Well-designed code is easy to change, refactoring is how you change from one design to the next, and tests free you to refactor with impunity.
  13. The solution to the problem of costly tests, however, is not to stop testing but instead to get better at it.
  14. It is an unfortunate truth that the most complex code is usually written by the least qualified person.

Site Footer