|
Motivation
Over the last few years I've found myself working with a large number of JavaScript code bases, both professionally and personally. In the course of creating and maintaining these projects I became increasingly dissatisfied with the various code reuse mechanisms available in the JavaScript language. These frustrations are probably best captured by the opening paragraphs of the research paper on which JSTraits is based: "Inheritance in object-oriented languages is well-established as an incremental modification mechanism that can be highly effective at enabling code reuse between similar classes. Unfortunately, single inheritance is inadequate for expressing classes that share features not inherited from their (unique) common parent. The shared features must either be forced into the common parent (where they do not belong), or they must be duplicated in the classes that should share them. To overcome this limitation, language designers have proposed various forms of multiple inheritance as well as other mechanisms, such as mixins, that allow classes to be composed incrementally from sets of features. "Despite the passage of nearly twenty years, neither multiple inheritance nor mixins have achieved wide acceptance. Summarizing Alan Snyder’s contribution to the inheritance panel discussion at OOPSLA ’87, Steve Cook wrote: “Multiple inheritance is good, but there is no good way to do it.” "Not only does multiple inheritance pose serious implementation problems, it is often inappropriate as a reuse mechanism: although multiple inheritance makes it possible to reuse a class (or a set of classes), a class is frequently not the element that one wishes to reuse. This is because classes play two competing roles. A class is primarilly a generator of instances. Therefore, most of the recent object-oriented programming languages such as Java and C# make every class bundle together a complete set of basic features by requiring it to be a (direct or indirect) subclass of the dedicated class Object. A class has a secondary role as a unit of reuse. It should therefore bundle a minimal set of features which can sensibly be reused together. Unfortunately these two roles conflict. Since classes must adopt a fixed position in the class hierarchy (i) it can be difficult or impossible to factor out wrapper methods (i.e., methods that extend other methods with additional functionality) as reusable classes, (ii) conflicting features inherited from different paths may be difficult to resolve, and (iii) overridden features may be difficult to access or compose. Perhaps for these reasons the designers of recent languages such as Java and C# decided that the complexities introduced by multiple inheritance outweigh its utility. "Flavors were an early attempt to address these problems: Flavors are small, incomplete implementations of classes, that can be “mixed in” at arbitrary places in the class hierarchy. More sophisticated notions of mixins were subsequently developed ... "Mixins use the ordinary single inheritance operator to extend various parent classes with the same set of features. Although this inheritance operator is well-suited for deriving new classes from existing ones, it is not necessarily appropriate for composing reusable building blocks. Specifically, because mixin composition is implemented using inheritance, mixins are composed linearly. This gives rise to several problems. First, a suitable total ordering of features may be difficult to find, or may not even exist. Second, “glue code” that exploits or adapts the linear composition may be dispersed throughout the class hierarchy. Third, the resulting class hierarchies are often fragile with respect to change, so that conceptually simple changes may impact many parts of the hierarchy. For these reasons, we believe, mixins have never achieved wide success in mainstream object-oriented languages. --- Traits: A Mechanism for Fine-grained Reuse - Stephane Ducasse, Oscar Nierstrasz, Nathanael Scharli, Roel Wuyts and Andrew P. Black Object Oriented Programming in JavaScriptJavaScript, being a prototype based language, does not directly provide single inheritance, multiple inheritance or mixins in the classical sense. However, similar functionality is achieved using variations on a technique known as "prototype chaining" and object merging. These pseudo-classical OOP facilities do help with code reuse in JavaScript, but unfortunately tend to lead to the exact same problems exhibited in their classical analogues as described in the extended quote above. Traits to the RescueQuoting again from the Traits paper: "Traits represent a simple solution to these various dilemmas. In a nutshell, a trait is a set of methods, divorced from any class hierarchy. Traits can be composed in arbitrary order. The composite entity has complete control over the composition and can resolve conflicts explicitly, without resorting to linearization. Classes are organized in a single inheritance hierarchy, and can make use of traits purely to specify the incremental difference in behavior with respect to their superclasses. This simple model has the following consequences. |