Decorator

Decorator is a structural pattern that consists of dynamically augmenting the behavior of an existing object. It's different from classical inheritance, because the behavior is not added to all the objects of the same class but only to the instances that are explicitly decorated.

Implementation-wise, it is very similar to the Proxy pattern, but instead of enhancing or modifying the behavior of the existing interface of an object, it augments it with new functionalities, as described in the following figure:

Decorator

In the previous figure, the Decorator object is extending the Component object by adding the methodC() operation. The existing methods are usually delegated to the decorated object, without further processing. Of course, if necessary we can easily combine the Proxy pattern, so that also the calls to the existing methods can be intercepted and manipulated.

Although Proxy and Decorator are conceptually two different patterns, with different intents, they practically share the same implementation strategies. Let's revise them.

Before we start coding with the next example, let's spend a few words to introduce LevelUP, the module that we are now going to work with.

LevelUP (https://npmjs.org/package/levelup) is a Node.js wrapper around Google's LevelDB, a key-value store originally built to implement IndexedDB in the Chrome browser, but it's much more than that. LevelDB has been defined by Dominic Tarr as the "Node.js of databases", because of its minimalism and extensibility. Like Node.js, LevelDB provides blazing fast performances and only the most basic set of features, allowing developers to build any kind of database on top of it.

The Node.js community, and in this case Rod Vagg, did not miss the chance to bring the power of this database into Node.js by creating LevelUP. Born as a wrapper for LevelDB, it then evolved to support several kinds of backends, from in-memory stores, to other NoSQL databases such as Riak and Redis, to web storage engines such as IndexedDB and localStorage, allowing to use the same API on both the server and the client, opening up some really interesting scenarios.

Today, there is a full-fledged ecosystem around LevelUp made of plugins and modules that extend the tiny core to implement features such as replication, secondary indexes, live updates, query engines, and more. Also, complete databases were built on top of LevelUP, including CouchDB clones such as PouchDB (https://npmjs.org/package/pouchdb) and CouchUP (https://npmjs.org/package/couchup), and even a graph database, levelgraph (https://npmjs.org/package/levelgraph) that can work both on Node.js and the browser!

In the next example, we are going to show how we can create a simple plugin for LevelUp using the Decorator pattern, and in particular, the object augmentation technique, which is the simplest but nonetheless the most pragmatic and effective way to decorate objects with additional capabilities.

Note

For convenience, we are going to use the level package (http://npmjs.org/package/level) that bundles both levelup and the default adapter called leveldown, which uses LevelDB as the backend.

What we want to build is a plugin for LevelUP that allows to receive notifications every time an object with a certain pattern is saved into the database. For example, if we subscribe to a pattern such as {a: 1}, we want to receive a notification when objects such as {a: 1, b: 3} or {a: 1, c: 'x'} are saved into the database.

Let's start to build our small plugin by creating a new module called levelSubscribe.js. We will then insert the following code:

module.exports = function levelSubscribe(db) {

  db.subscribe = function(pattern, listener) {       //[1]
    db.on('put', function(key, val) {         //[2]
      var match = Object.keys(pattern).every(function(k) { //[3]
        return pattern[k] === val[k];
      });
      if(match) {
        listener(key, val);            //[4]
      }
    });
  };

  return db;
}

That's it for our plugin, and it's extremely simple. Let's see what happens in the preceding code briefly:

  1. We decorated the db object with a new method named subscribe(). We simply attached the method directly to the provided db instance (object augmentation).
  2. We listen for any put operation performed on the database.
  3. We performed a very simple pattern-matching algorithm, which verified that all the properties in the provided pattern are also available on the data being inserted.
  4. If we have a match, we notify the listener.

Let's now create some code—in a new file named levelSubscribeTest.js—to try out our new plugin:

This is what we did in the preceding code:

This example shows a real application of the decorator pattern in its most simple implementation: object augmentation. It might look like a trivial pattern but it has undoubted power if used appropriately.

For more examples of how Decorator is used in the real world, we might want to inspect the code of some more LevelUp plugins: