In JavaScript hat das wohl meist bekannte (und unterbewusst am häufigsten verwendete) Entwurfsmuster, das Singleton, einen großen Nachteil. Beim Laden der Seite werden alle Skripte initialisiert und die Singleton Klassen somit sofort ausgeführt. Wenn ein Singleton eine aufwendige Initialisierungs-Methode bzw. sehr ressourcenintensiv ist, kann es Sinn machen die Instanziierung auf einen späteren Zeitpunkt zu verschieben, beispielsweise erst dann wenn es das erste Mal verwendet wird.

Wie das mittels Lazy Instantiation in JavaScript funktioniert zeigt dieser Artikel.

Gehen wir von einer einfachen Klasse, implementiert nach dem Singleton Muster, aus:
Dabei verwenden wir eine Closure um private und öffentliche Elemente zu erzeugen, wie im Artikel JavaScript: How to create class with private elements? gezeigt.
Außerdem wird das Verständnis von immediate functions (JavaScript: (Immediate Function)(); ?!) vorausgesetzt.

var MyNamespace = {};
MyNamespace.MyClass = (function() {
  // Private...
  var privateVal1;
  var privateVal2;

  function privateMethod1() {
    // Do something...
  }

  function privateMethod2() {
    // Do something...
  }

  return {
    publicVal1: null,
    publicVal2: null,

    publicMethod1: function() {
      // Do something...
    },

    public Method2: function() {
      // Do something...
    }
  };

})();

Natürlich ist dies ein schlechtes Beispiel, da wir keine ressourcenintensive Operationen oder aufwendigen Initialisierungscode in diesem Beispiel haben. Aber es soll nur den Aufbau von Lazy Instantiation zeigen.

Um den oben gezeigten Code in ein Lazy Instantiation zu verwandeln, setzen wir erst einmal das Grundgerüst für Lazy Instantiation auf:

var MyNamespace = windows.MyNamespace || {};
MyNamespace.LazyInstantiation = (function() {

  var thisInstance;

  function constructor() {
    // Code...
  }

  return {
    getInstance: function() {
      if (!thisInstance) {
        thisInstance = constructor();
      }

      return thisInstance;
    }
  };

})();

An diesem Grundgerüst wird das Singleton Pattern übrigens sehr deutlich gemacht.
Dieser Code Schnippsel kann zum Erzeugen einer Lazy Instantiation Klasse immer wiederverwendet werden. Um unser vorheriges Beispiel nun in ein Lazy Instantiation zu verwandeln, genügt es den gesamten Code in die constructor() Funktion zu kopieren.

Das ganze sieht nun folgendermaßen aus:

var MyNamespace = windows.MyNamespace || {};
MyNamespace.LazyInstantiation = (function() {

  var thisInstance;

  function constructor() {
    // Private...
    var privateVal1;
    var privateVal2;

    function privateMethod1() {
      // Do something...
    }

    function privateMethod2() {
      // Do something...
    }

    return {
      publicVal1: null,
      publicVal2: null,

      publicMethod1: function() {
        // Do something...
      },

      public Method2: function() {
        // Do something...
      }
    };
  }

  return {
    getInstance: function() {
      if (!thisInstance) {
        thisInstance = constructor();
      }

      return thisInstance;
    }
  };

})();

Kurze Erklärung?
Kommen wir erstmal zur Verwendung des Codes, da dieser nicht auf Anhieb ersichtlich sein wird.

MyNamespace.LazyInstantiation.publicMethod1(); // Funktioniert nicht!
MyNamespace.LazyInstantiation.getInstance().publicMethod1(); // Funktioniert!

Beim Initialisieren des Skripts erzeugt lediglich die private Variable namens thisInstance und gibt ein Objektliteral zurück. Von außen ist nun lediglich der Zugriff auf die öffentliche Methode namens getInstance() möglich. Beim ersten Aufruf dieser getInstance() Methode wird der privaten Variablen thisInstance der Rückgabewert von der privaten Methode constructor() zugewiesen.
Die constructor() Methode enthält den gesamten (!) vorherigen und eigentlichen Code der Klasse.

Ein Nachteil von Lazy Instantiation ist die gestiegene Komplexität der Klasse. Eine solche Klasse sollte ausreichend dokumentiert werden sodass diese von einem anderen Entwickler auch als eine “Lazy Instantiation Singleton” Klasse verstanden wird. Der umständlich lange Aufruf von ….getInstance()… kann mit einem Alias umgangen werden.

var MyObj = MyNamespace.LazyInstantiation.getInstance();
MyObj.publicMethod1(); // Funktioniert

 


0 Comments

Leave a Reply