Das Revealing Module Pattern ist eine bewährte Methode, um JavaScript-Code zu kapseln und eine private Implementierung hinter einer definierten öffentlichen Schnittstelle zu verbergen. In diesem Artikel werde ich zeigen, wie man das Revealing Module Pattern in Node.js verwendet und wie man so ein Modul mit http://usejsdoc.org/.

Als ersten Schritt implementieren wir ein generisches Modul nach den Richtlinien des Revealing Module Pattern:

var katzenModul = (function () {  
    var privaterName = 'kitty';

    function publicGetName(){
        return privaterName;
    }

    return {
        getName : publicGetName
    };
})();

katzenModul.getName();  

Dieses Modul definiert eine Funktion publicGetName(), die nach aussen als getName deklariert wird. Der Name der Katze ist privat und kann nicht von aussen zugegriffen werden, wie die entsprechenden Tests beweisen:

describe('katzenModul', function() {

  it('should return the name of the cat', function() {
      var name = katzenModul.getName();
      assert.equal(name, 'kitty');
  });

  it('should not allow access to private variables', function() {
      assert.equal( undefined, katzenModul.privaterName );
  });

  it('should not allow access to private functions', function() {
      assert.equal( undefined, katzenModul.publicGetName );
  });
});

Als nächstes machen wir aus unserem generischen Modul ein Node-Modul:

var katzenModul = (function () {  
    var privaterName = 'kitty';

    function publicGetName(){
        return privaterName;
    }

    return {
        getName : publicGetName
    };
})();
module.exports = katzenModul;  

Durch die letzte Zeile module.exports = katzenModul; haben wir unser Modul Node.js bekannt gemacht. Nun können wir das Modul z.B. wie folgt verwenden:

var katzenModul = require('./katzenModul');

console.log( katzenModul.getName() );  

Ein weiterer Vorteil des Revealing Module Pattern ist, dass es sich sehr gut eignet, um Dokumentation zu schreiben, ohne die Lesbarkeit des Codes zu beeinträchtigen. Der Grund dafür ist, dass die Dokumentation der "öffentlichen" Schnittstelle eines solchen Moduls am Ende der Datei steht und nicht den eigentlichen Code "verunreinigt". So sieht z.B. eine dokumentierte Version von katzenModul aus:

/**
 * @description Dieses Module liefert Katzennamen
 * @module katzenModul
 * @author Marcelo Emmerich
 */
var katzenModul = (function () {  
    var privaterName = 'kitty';

    function publicGetName(){
        return privaterName;
    }

    return {
        /**
         * @function
         * @description Liefert den Namen der Katze
         * @memberof module:katzenModul
         * @returns {String} Den Namen der Katze als String
         */
        getName : publicGetName
    };
})();
module.exports = katzenModul;  

Zuerst habe ich das Modul als solches mit dem JSDoc Tag @module definiert, um es später referenzieren zu können. @author und @description sind vermutlich selbst erklärend, also schauen wir uns die interessanteren Tags an. Die Funktion getName ist mit @function markiert. Das ist deswegen notwendig, weil der JSDoc Parser sonst nicht mit der Syntax des Revealing Module Pattern klar kommt und die Funktion nicht als solche erkennt. Weiter müssen wir JSDoc auch damit auf die Sprünge helfen, die Funktion getName als zum Modul katzenModul zugehörig anzuerkennen. Das erreichen wir mit dem @memberof module:<Modulname> Tag. Wenn wir nun mit JSDoc die Dokumentation erzeugen, wird unser Modul als solches aufgeführt und die Funktion getName korrekt referenziert.