Class mixins

Backbone provides a very powerful class system, but sometimes, even this class system isn't enough. For instance, let's say you have several classes that all contain several identical methods. The popular Don't Repeat Yourself (DRY) programming principle suggests that you should eliminate the duplicate versions of the methods and instead, define them all together in just one place in your code.

Normally, you could do so by refactoring these methods into a common parent class of these classes. But what if these classes can't share a common parent class? For instance, what if one of these classes is a Model class and the other a Collection class?

In this case, you need to rely on something called a mixin. A mixin is just an object that holds one or more methods (or even primitive properties) that you want to share between several classes. For example, if we wanted to share a couple of logging-related methods between several classes, we could create a mixin of these methods, as follows:

var loggingMixin = {
    startLogging: function() {
        Logger.startLogging(this);
    },
    stopLogging: function() {
        Logger.stopLogging(this);
    }
}

Once you've defined your mixin, the next step is to "mix it in" to the definition of your class. This means modifying the prototype of the class to include the methods from our mixin. For instance, let's say we have a book Model:

var Book = Backbone.Model.extend({
    defaults: {currentPage: 1},

    read: function() {
        // TODO: add logic to read a book here
    }
});

Because Backbone's syntax makes creating our new class so simple, it's easy to miss the fact that we're actually defining a prototype object here, but if we change things just a little, it suddenly becomes more obvious:

var bookPrototype = {
    defaults: {currentPage: 1},

    read: function() {
        // TODO: add logic to read a book here
    }
};Book = Backbone.Model.extend(bookPrototype);

Once we've separated the Book prototype, we can augment it with our mixin. We could do this manually:

bookPrototype.startLogging = loggingMixin.startLogging;
bookPrototype.stopLogging = loggingMixin.stopLogging;

However, this won't work well if we have a lot of methods to mixin, or if our mixin later gets new methods added. Instead, we can use Underscore's extend function to extend all of the methods in our mixin on to the Book prototype in a single command: _.extend(bookPrototype, loggingMixin);

With just this one line, we've added a whole set of logging-related methods to our Book class. Plus, we can now share these methods with any other class, again by using only a single line, all without having to change the parent class of any of the classes involved.

Of course, it's important to note that because we are applying our mixin on top of the existing prototype, it will overwrite any properties that it has in common with the prototype. In other words, assume that our Book class already has its own startLogging method that prevents logging:

bookPrototype = {
    defaults: {currentPage: 1},

    read: function() {
        // TODO: add logic to read a book here
    },
    startLogging: $.noop // don't log this class
};

We will erase this method by applying our mixin, giving us logging on our class even though we don't want it. Further, even if Book doesn't have such a method, a future developer might add one and then, be confused when it doesn't work. If the future developer doesn't see (or doesn't understand) the mixin line, he might spend hours looking through the parent classes of Book, trying to figure out what's happening.

Because of this issue, it is usually better to rely on standard object-oriented programming as much as possible when designing your classes, and only use mixins when you can't share methods through a normal class hierarchy. In these cases, however, mixins can be a powerful tool and serve as yet another example of what is possible in JavaScript but not in most other languages (Good luck mixing methods into a Java class!).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.166.149