[Mod]ule Culture

Malina Tran
Tech and the City
Published in
3 min readJun 14, 2016

This is powerful stuff, and dangerous in untutored hands. However, because this very same power is what allows you to create simple structures of related objects that elegantly fulfill the needs of your application, your task is not to avoid these techniques but to learn to use them for the right reasons, in the right places, in the correct way.

- Sandi Metz, Practical Object Oriented Design in Ruby, Chapter 7

Mod culture was exemplified by Italian motor scooters; I thought the likeness of these scooters also demonstrated modules in a transportation context.

The more I read POODR, the more I realize that the complexity in code is largely due to the relationships and interactions between objects. I know, what a grossly obvious statement. But, objects have private and public methods; they rely on other pieces of code; they need to not know too much. All of these rules and principles dictating how objects work make it incredibly difficult albeit interesting to both play by the rules and writing sensible code.

With duck typing and inheritance, the focus was on abstracting code in such a way that still renders them useful (and reusable) by classes. What a module accomplishes is the sharing of behavior–through methods, classes, and constants–among seemingly unrelated objects. The methods within a module are independent of class and can be applied toward any object.

The benefits of modules are apparent: they provide a namespace and prevent any potential naming conflicts and therefore accidental overriding. Modules also remove the need for multiple inheritance, which is a feature that exists in other object-oriented programming languages but is not supported by Ruby. This is known as a “mixin,” which is a class that contains methods for use by other classes without needing to be a parent class or superclass. And, once again, this technique minimizes the amount of code to be written.

Yet, writing code into modules and subsequently adding them to objects amp up the number of messages. It brings even more complexity. Here are Metz’s guidelines for writing code that is inheritable:

  • If duck types need to share behavior, that code can belong in a module and should be included in any class object that requires it.
  • Superclasses should contain code that applies to all of its subclasses, and not be overridden.
  • Ideally, subclasses can be used in place of its respective superclass (per Liskov Substitution Principle).
  • Use hook messages over sending of `super`. Writing code that requires subclasses to send `super` adds additional dependency and should be avoided.
  • Create relationships that exemplify a shallow hierarchy. You can think of depth as determined by number of superclasses and breadth by its direct subclasses. Shallow, narrow hierarchies are easier to understand since their relationships are not very complex. See image below.
Shallow and narrow hierarchies will do the trick!

At the most practical level, modules provide two keywords. The first is `require <filename>` which I’ve used liberally without associating with modules. This simply loads relevant files. The second is `include <modulename>`, which differentiates itself by embedding a module in a class. If the module belongs in another file, it will need to also have the require statement. Modules offer an alternative to inheritance by sharing roles. And the code defined in a module can be added to any object, whether it is the class or an instance of the class, or even another module. This kind of flexibility ensures that we continue writing code that is modular and pluggable, that can shape and shift as the application takes life.

--

--