Klasse nur mit Konstruktor

Singletons sollten wenn möglich weitesgehend vermieden werden, da man an eine konkrete Klasse gebunden ist und man keine Interfaces benutzen kann. (Sowas sorgt fuer schlechte Testbarkeit, Erweiterbarkeit etcpp.)

Wieso sollte man von Singleton-Klassen keine Interfaces erstellen können

Java:
public interface DatabaseSingleton {
    public static DatabaseSingleton getInstance();
    public ResultSet getData();
}

public class RealDatabaseSingleton implements DatabaseSingleton {
    // was man halt so braucht
    public static DatabaseSingleton getInstance() {
         return (DatabaseSingleton) realDatabaseSingletonObject;
    }
    public ResultSet getData() {
         return RealDatabaseSingletonObject.getQueryResult();
    }
}

public class TestDatabaseSingleton implements DatabaseSingleton {
    // was man halt so braucht
    public static DatabaseSingleton getInstance() {
         return (DatabaseSingleton) testDatabaseSingletonObject;
    }
    public ResultSet getData() {
         return deserializedTestObject();
    }
}
 
Wieso sollte man von Singleton-Klassen keine Interfaces erstellen können

Java:
public interface DatabaseSingleton {
    public static DatabaseSingleton getInstance();
    public ResultSet getData();
}

public class RealDatabaseSingleton implements DatabaseSingleton {
    // was man halt so braucht
    public static DatabaseSingleton getInstance() {
         return (DatabaseSingleton) realDatabaseSingletonObject;
    }
    public ResultSet getData() {
         return RealDatabaseSingletonObject.getQueryResult();
    }
}

public class TestDatabaseSingleton implements DatabaseSingleton {
    // was man halt so braucht
    public static DatabaseSingleton getInstance() {
         return (DatabaseSingleton) testDatabaseSingletonObject;
    }
    public ResultSet getData() {
         return deserializedTestObject();
    }
}

Dieser Code wird nicht kompilierbar sein, da sich statische Methode nicht vererben lassen und auch nicht überschrieben werden, da sie an die konkrete Klasse gebunden sind.

Selbst wenn er kompilierbar wäre sag bitte welche Implementierung er beim Aufruf von

Java:
DatabaseSingleton.getInstance();

zurueck geben sollte. Die zuerst Gefundene, eine zufällige Auswahl oder wird das nach dem Round Robin Prinzip genutzt?
 
Zuletzt bearbeitet:
Dieser Code wird nicht kompilierbar sein, da sich statische Methode nicht vererben lassen und auch nicht überschrieben werden, da sie an die konkrete Klasse gebunden sind.

Stimme ich dir 100 % zu, habe ich im Eifer des Gefechts nicht dran gedacht. Allerdings ist das die einzige Methode, die wirklich für den Singleton gebraucht wird. Es gibt also keinen Grund, warum man diese Methode nicht Template-mäßig in allen benötigten Klassen kopieren sollte (evtl. ließe sich über Reflections noch was machen). Der "unschöne" Pflegeaufwand beschränkt sich somit rein auf diese eine Methode, der Rest der Klassen wäre über Interfaces sauber abgebildet.

Selbst wenn er kompilierbar wäre sag bitte welche Implementierung er beim Aufruf von
java Code:
DatabaseSingleton.getInstance();
zurueck geben sollte. Die zuerst Gefundene, eine zufällige Auswahl oder wird das nach dem Round Robin Prinzip genutzt?

Na das würde man ja wohl auch kaum wollen oder?
Ersten kannst von einem Interface keine Methoden aufrufen und zweitens wäre der Sinn ja folgender:

Java:
DatabaseSingleton database = RealDatabaseSingleton.getInstance();

Das ganze in einer Factory und schon hast du nur eine Stelle, an der du vom Echt- in den Testbetrieb umschalten kannst.
 
In dem Fall sollte die Singleton Funktionalität in der Factory (Instanzverwaltung der Datenbankinstanz; Selektion der der Datenbankimplementierung) liegen und nicht in der Datenbankklasse (Bereitstellung der wie auch immer gearteten Datenbankfunktionen).

Aber ansonsten geben sich dein Konzept der Implementierung und meins nicht wirklich viel.
 
@shutdon:
Und? Dein Clientcode hat dann eine Abhängigkeit zur Implementierung. Dann nützt dir das Interface auch nichts mehr. Wie gestaltest du denn dann das "Umschalten"? In der Implementierung? Toll, dann hast du dort Logik, die die Umgebung unterscheidet.

Viel schlimmer ist eigentlich noch, dass die Abhängigkeit zu RealDatabaseSingleton nirgends an der SChnittstelle des Clients erkenntlich ist.

Dependency Injection löst das Problem indem du Konstruktorparameter oder Setter einführst, mit denen eine Komponente Abhängigkeiten ausdrückt.

Java:
public class DatabaseClient {

  private DatabaseSingleton foo;

  public DatabaseClient(DatabaseSingleton foo) {
    this.foo = foo;
  }
}

So ist an der SChnittstelle der Klasse erkennbar, welche Kollaborateure sie benötigt, zum anderen ist sie wirklich nur noch an das Interface gekoppelt.

Gruß
Ollie
 
Und? Dein Clientcode hat dann eine Abhängigkeit zur Implementierung. Dann nützt dir das Interface auch nichts mehr. Wie gestaltest du denn dann das "Umschalten"? In der Implementierung? Toll, dann hast du dort Logik, die die Umgebung unterscheidet.

Im Grunde die gleiche Abhängigkeit, die dein Konstruktor auch aufbaut, die Abhängigkeit zu einem DatabaseSingleton, einem Interface, keiner echten Implementierung. Das Umschalten könnte, wie schon gesagt, in einer Factory geschehen. Ob jetzt durch hartes Codieren oder durch externe Parameter sei mal dahingestellt.

Viel schlimmer ist eigentlich noch, dass die Abhängigkeit zu RealDatabaseSingleton nirgends an der SChnittstelle des Clients erkenntlich ist.

Das sehe ich ein - auch wenn mir Eclipse das Fehlen einer Klasse bisher immer schön unterringelt hat, und der Client vom RealDatabaseSingleton ja eigentlich überhaupt nichts wissen soll :)
 
Der Unterschied ist aber, dass dein Client in deiner Variante von RealDatabaseSingleton abhängt, ohne das er das weiß. D.h. z.B. dass du den Client NIE testen kannst, ohne implizit RealDatabaseSingleton mitzutesten. In der von mir vorgeschlagenen Variante mockst du einfach das Interface und gut ist.

Das ist ein subtiler Unterschied, der große Wirkung hat. In "echten" Applikationen ist nämlich in RealDatabaseSingleton sicher nicht schluss mit den Dependencies. Angenommen RealDatabaseSingleton kommt per JNDI Lookup an die Datasource. Das ist Quasi das KO für jeden (Unit)Test für den Client bzw. den Test ausserhalb eines Containers. Sowas geht halt gar nicht.

Der andere Punkt ist, dass dir Eclipse zwar das Fehlen anzeigen würde, zur Laufzeit allerdings das fehlen der Dependency mit ner ClassNotFoundException quittiert wird. D.h. du kannst den Client NIE wieder ohne RealDatabaseSingleton verwenden. Vor allem wie eben geschildert beim Testen.

Hm, wozu hast du denn dann eigentlich noch das Interface eingefüht? :D

Gruß
Ollie
 
Ich sehe deine Argumente ein, allerdings sind meine Projekte so groß noch nicht geworden :)

Hm, wozu hast du denn dann eigentlich noch das Interface eingefüht?

Ich versuche meine Klassen so zu schreiben, dass ich möglichst viel davon in externe Jars auslagern kann, die dann wieder verwendet werden. Ich hab viele kleine Projekte, die wenn man mal alle Klassen zusammennimmt jedes so etwa 10 bis 20 Klassen hat.
Davon sind aber nur etwa 5 maximal tatsächlich Bestandteil des Projekts. Der Rest liegt als wiederverwertbarer Code vor. Und das Interface ermöglicht es z.B. die Art der Datenbankverbindung problemlos auszutauschen, im einen Fall über ODBC (ja, ich weiß, ganz pfui, muss aber manchmal sein :) ), im anderen über JDBC oder wie auch immer.
 
Irgendwie argumentierst du grad für mich. Wenn du die Klassen wirklich in verschiedene Jars verteilst, DARFST du keine impliziten Abhängigkeiten haben. Dein Client Jar benötigt bei deinem Design Client, DatabaseSingleton UND RealDatabaseSingleton. D.h du schleppst eine Klasse mit durch die Gegend die da eigentlich nicht sein müsste / dürfte.

Es geht bei sowas auch nciht darum, wie groß ein Projekt wird. Auch jedes kleine Projekt sollte geunittestet werden. Und das setzt nunmal vorraus, dass ich (auch noch so kleine Komponenten) möglichst leicht mocken kann.

Gruß
Ollie
 
Zurück