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)