Traits and Mixins Are Not OOP

Let me say right off the bat that the features we will discuss here are pure poison brought to object-oriented programming by those who desperately needed a lobotomy, just like David West suggested in his Object Thinking book. These features have different names, but the most common ones are traits and mixins. I seriously can’t understand how we can still call programming object-oriented when it has these features.

Fear and Loathing in Las Vegas (1998) by Terry Gilliam

First, here’s how they work in a nutshell. Let’s use Ruby modules as a sample implementation. Say that we have a class Book:

classBookdefinitialize(title)@title=titleendend

Now, we want class Book to use a static method (a procedure) that does something useful. We may either define it in a utility class and let Book call it:

classTextUtilsdefself.caps(text)text.split.map(&:capitalize).join(' ')endendclassBookdefprintputs"My title is #{TextUtils.caps(@title)}"endend

Or we may make it even more “convenient” and extend our module in order to access its methods directly:

moduleTextModuledefcaps(text)text.split.map(&:capitalize).join(' ')endendclassBookextendTextModuledefprintputs"My title is #{caps(@title)}"endend

It seems nice—if you don’t understand the difference between object-oriented programming and static methods. Moreover, if we forget OOP purity for a minute, this approach actually looks less readable to me, even though it has fewer characters; it’s difficult to understand where the method caps() is coming from when it’s called just like #{caps(@title)} instead of #{TextUtils.caps(@title)}. Don’t you think?

Mixins start to play their role better when we include them. We can combine them to construct the behavior of the class we’re looking for. Let’s create two mixins. The first one will be called PlainMixin and will print the title of the book the way it is, and the second one will be called CapsMixin and will capitalize what’s already printed:

moduleCapsMixindefto_ssuper.to_s.split.map(&:capitalize).join(' ')endendmodulePlainMixindefto_s@titleendendclassBookdefinitialize(title)@title=titleendincludeCapsMixin,PlainMixindefprintputs"My title is #{self}"endend

Calling Book without the included mixin will print its title the way it is. Once we add the include statement, the behavior of to_s is overridden and method print produces a different result. We can combine mixins to produce the required functionality. For example, we can add one more, which will abbreviate the title to 16 characters:

moduleAbbrMixindefto_ssuper.to_s.gsub(/^(.{16,}?).*$/m,'\1...')endendclassBookdefinitialize(title)@title=titleendincludeAbbrMixin,CapsMixin,PlainMixindefprintputs"My title is #{self}"endend

I’m sure you already understand that they both have access to the private attribute @title of class Book. They actually have full access to everything in the class. They literally are “pieces of code” that we inject into the class to make it more powerful and complex. What’s wrong with this approach?

In the case of mixins, the functionality is in the Ruby modules, which make assumptions about the internal structure of Book and further assume that the programmer will still understand what’s in Book after the internal structure changes. Such assumptions completely violate the very idea of encapsulation.

Such a tight coupling between mixins and object private structure leads to nothing but unmaintainable and difficult to understand code.

Doesn’t it look very similar to what we were doing above with Ruby mixins?

However, unlike mixins, decorators leave objects small and cohesive, layering extra functionality on top of them. Mixins do the opposite—they make objects more complex and, thanks to that, less readable and maintainable.

I honestly believe they are just poison. Whoever invented them was a long ways from understanding the philosophy of object-oriented design.