Feb 22

JavaScript OOP: patterns

Tag: AllgemeinPhoscur @ 03:50

Patterns – Muster auf Deutsch. Informatiker fassen gerne Probleme in Muster um sie zu katalogisieren. Eine Problemlösung kann so öfter wieder verwendet werden, Fehler werden vermieden. Manchmal stellen sie auch einfach einen Weg dar etwas elegant zu lösen. Besonders in objektorientierter Programmierung sind solche Muster bekannt: Entwurfsmuster.

Nachdem ich mich jetzt während des Sommers mit Objektorientierung, Softwarearchitektur und Entwurfsmustern beschäftigt hatte, suchte ich auch in JavaScript nach Mustern und Best Practice, und  nun möchte ich diese Erfahrungen auf meinem Blog teilen.

Vorab sollte erwähnt werden, dass JavaScript in mehreren Aspekten eine besondere Sprache ist. Es wird nicht umsonst geschmäht aufgrund unerklärlichen Verhaltens. Man sehe sich nur mal wtfjs an! Dennoch ist JavaScript auch sehr mächtig als objektorientierte Sprache. Closures und Prototypen sind recht unbekannt in der sehr von Java geprägten objektorientierten Welt. Die Umgewöhnung kann schwer fallen, auch ich hatte da meine Probleme. Zum Glück gibt es zum Beispiel Videos von Douglas Crockford auf YUI Theater, die einem helfen einen Einstieg zu finden.

Ich möchte nun hier zwei Funktionen vorstellen, die es auch in die neue ECMAScript Spezifikation geschafft haben: Function.prototype.bind und Object.create. Beide lassen sich mit einigen Tricks auch auf dem momentan verbreiteten JavaScript verwenden, bei Frameworks gehören sie zum Grundrepertoire.

Viel kann ich noch nicht zu prototypenbasierter Programmierung sagen, da ich selbst noch nicht ganz auf den Trichter gekommen bin, trotzdem habe ich schon Anwendungen gefunden und ziehe Nutzen in meinem sonst noch recht klassenbasierten Stil.

Object.create

if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

Diese schöne Funktion erzeugt ein Objekt auf Basis eines anderen Objekts, welches zum Prototypen wird. Vorsichtig muss man sein, wenn man lokale/private Variablen im Prototypen verwendet, da sich die beiden Objekte diese teilen. Der Prototyp sollte also ein neues Objekt sein, dass sonst nicht genutzt wird, oder die vermeintlichen Methoden müssen neu initialisiert werden, wie ich es in vorigem Blogeintrag mit dem aufrufen eines Konstruktors mache.

Ich möchte das Beispiel auch nochmal aufgreifen, wobei ich mittlerweile nochmal refaktorisiert habe.

Resource.prototype.create = function(newAmount) {
    var newResource = Object.create(this);
    Resource.call(newResource, newAmount, this.getType());
    return newResource;
};

Mit dieser Methode ist es möglich Objekte der selben Resource anhand einer vorliegenden zu erstellen. Theoretisch könnte ich diese Methode auch clone() nennen, da ich aber gleichzeitig das neue Objekt initialisiere, behalte ich den namen der Basisfunktion – create(). clone() lässt sich damit aber recht leicht verwirklichen, man muss nur die Eigenschaften vom alten Objekt übergeben.

Resource.prototype.clone = function() {
    return this.create(this.getAmount());
}

Schön, jetzt habe ich ein paar tolle neue Möglichkeiten Objekte zu erzeugen, was soll das nun?
Unter den Entwurfsmustern gibt es eine Untermenge: Creational Patterns, Muster zur Konstruktion. Einige Muster, die sich mit verschiedenen Möglichkeiten der Konstruktion von Objekten beschäftigen. Alle vermeiden die Konstruktion mittels „new <Klassenname>“, weil dies (unter anderem) zu statisch ist.
Wenn ich ein Objekt mit einer create() Methode übergebe, dann übergebe ich gleichzeitig die Möglichkeit ein neues Objekt des selben Typs zu erstellen, ohne mich explizit an den Typ zu binden. Die Konstruktion ist dynamisch. Aus diesem Grund kann ich auch leicht Vererbung anwenden ohne danach meinen ganzen Code nach „new <Elternklassenname>“ durchsuchen zu müssen.

Function.prototype.bind

if (typeof Function.prototype.bind !== "function") {
    Function.prototype.bind = function(context) {
        if (arguments.length < 2 && typeof arguments[0] === "undefined") {
            return this;
        }
        var method = this, args = Array.prototype.slice.call(arguments, 1);
        return function() {
            var a = merge(args, arguments);
            return method.apply(context, a);
        };
    };
}

Den Code dieser Funktion habe ich mir aus dem Prototype Framework geholt und von Abhängigkeiten befreit. Jede Funktion und insbesondere Methode ist ab jetzt mit der Funktionalität ausgerüstet, unabhängig von ihrem Objekt gemacht zu werden.
Schonmal das probiert?

var func = someObject.someMethod;
func();

Das geht schwer in die Hose wenn someMethod „this“ verwendet, „this“ is dann nämlich „window“ – eine der Freuden an JavaScript.
Stattdessen verwendet man besser:

var func = someObject.someMethod.bind();

Theoretisch kann man auch bereits Parameter mitgeben.
Diese Funktion findet häufig Verwendung, wenn man bei AJAX oder Effekten Callbacks übergibt und spart jedes Mal manuelles Umwickeln mit einer anonymen Funktion.

Die Tiefe von Patterns habe ich mit diesem Artikel wohl nicht erreicht, aber ich hoffe er ist trotzdem einigen Leuten hilfreich. Die vorgestellten Funktionen würde man wohl als Sugar („Zucker“) bezeichnen.

Ein Kommentar zu “JavaScript OOP: patterns”

  1. Malocher schrieb:

    Alter Schwede, der Artikel zeigt mir mal wieder wie wenig ich bisher von Programmierung verstehe.

Dein Kommentar

Du musst eingelogt sein, um einen Kommentar zu schreiben.