JavaScript und Design-Patterns
Im Rahmen meiner Vorbereitung auf den Workshop “Design-Patterns” habe ich mir so einige Gedanken zum Thema Singleton gemacht.
Singleton und Java
Das Thema Singleton in Java ist so ziemlich ausgereizt. So bevorzuge ich die “Bill-Pugh-Solution”:
public class BillPughSingleton {
private BillPughSingleton() {}
private static class LazyHolder {
private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}
public static BillPughSingleton getInstance() {
return LazyHolder.INSTANCE;
}
}Ich liebe einfach “lazy”…
Allerdings gibt es bei der Lösung eine kleine Unschönheit: Man kann die Eigenschaft
“Singleton” nicht so einfach vererben! Ein Ableiten der Klasse erlaubt es leider nicht
die Methode getInstance() in Java nicht überschreiben und dabei den Rückgabetyp verändern!
Man müsste daher entweder:
- Ein Typ-Cast machen, (Häßlich!)
- oder eine neue Methode
getAnotherInstance()implementieren, die den Type-Cast versteckt.
Beides ist nicht schön!
Die Alternative mit “generischen Typen” zu arbeiten ist leider auch nicht nett, denn
es gibt kein public static <T> T get() { return new T(); } in Java. Man müsste daher eher
mit Reflection arbeiten, was zwar geht… aber nicht schön ist!
Singleton und Python
In Python kann man daher das “Borg-Pattern” verwenden, welches die Vererbung der Singleton-Eigenschaft ermöglicht.
class Borg:
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state
# and whatever else you want in your class -- that's all!Der “Trick” liegt hier im überschreiben der __dict__ welches in Python alle
Attribute und Methoden einer Klasse enthält.
Singleton und C++
In C++ kann man schon eher mit generischen Typen arbeiten:
template<typename T>
class Singleton
{
private:
static shared_ptr<T> _instance;
protected:
Singleton() {}
public:
virtual ~Singleton() {}
static shared_ptr<T> instance();
};
template<typename T> // (1)
shared_ptr<T> Singleton<T>::_instance = nullptr;
template<typename T> // (2)
shared_ptr<T> Singleton<T>::instance()
{
if (Singleton<T>::_instance.get() == nullptr) Singleton<T>::_instance = shared_ptr<T>(new T());
return Singleton<T>::_instance;
}Wichtig ist, dass die beiden Definitionen (1) und (2) nicht in einer CPP-Datei
landen, da die Singleton-Klasse ein Template ist und daher keinen OBJ-Code erzeugt
(bzw. erzeugen darf!)
Danach kann man die Klasse als Basis-Klasse verwenden, muss aber darauf achten, dass
die Singleton-Basis-Klasse als friend deklariert wird.
class HelloWorld : public Singleton<HelloWorld>
{
friend class Singleton<HelloWorld>;
protected:
string greeting_;
HelloWorld() : greeting_("Hello, world!") {}
public:
virtual ~HelloWorld() } {}
virtual string greeting() const { return greeting_; }
};Naja, oder man benutzt die Boost-Bibliothek:
#include <boost/serialization/singleton.hpp>
using namespace boost::serialization;
class MySingleton : public singleton<MySingleton>
{
public:
int i;
};
int main()
{
MySingleton::get_mutable_instance().i = 42;
}Singleton und JavaScript
Mein Wissen und meine Erfahrung bzgl. JavaScript sind doch sehr begrenzt, aber natürlich bin ich, als alter Compilerbauer, an der Sprache interessiert.
Dies ist mein erster Entwurf für das Singleton-Pattern in JavaScript:
function Singleton() {
Singleton.instance = this;
Singleton.instance.value = 42
}
Singleton.prototype = {
constructor: Singleton,
instance : function() {
return Singleton.instance;
},
sayHello : function() {
console.log("Hello, singleton:" + this.instance().value);
}
}
var s = new Singleton();
s.instance().sayHello();
var p = new Singleton();
p.instance().value = 45;
p.instance().sayHello();
s.instance().sayHello();
console.log(s.instance() === p.instance());
console.log(s === p);Und tatsächlich kommt auf der Console heraus, dass s.instance() === p.instance() ist
und s === p nicht, was für eine interessante Konstellation sorgt. Ich habe hier also
quasi ein Singleton, welches ich aber noch “anreichern” könnte. Schliesslich hole
ich die (Singleton-)Instanz nicht “über die Klasse”, sondern über “neue” Instanz. Daher
ist auch p ungleich s während die instance gleich ist.
Allerdings ist zwar die instance() ‘Singleton’, aber der ‘Holder’ ist es nicht. Darum
geht es besser:
function SingletonClass() {
if (arguments.callee.instance)
return arguments.callee.instance;
arguments.callee.instance = this;
}
SingletonClass.getInstance = function () {
var singletonClass = new SingletonClass();
return singletonClass;
}Zugriff auf die Singleton geht dann mit var singleton = SingletonClass.getInstance(). (Code
gefunden auf Stackoverflow)