Sollte ein DAO ein Singleton sein oder nicht?

14

Ich entwickle eine RESTful-API und halte es für zweckmäßig, DAOs für meine Ressourcen zu verwenden, da ich, obwohl ich beabsichtige, nur Speicher zum Speichern dieser Ressourcen zu verwenden, nicht die Tür zu jedem schließen möchte, der meine Bibliothek verwendet, wenn er dies möchte eine Datenbankimplementierung für das DAO.

Meine Frage ist, ob das DAO ein Singleton sein soll oder nicht. Ist dies nicht der Fall, verfügt der Dienst über eine Instanz des DAO und sieht ungefähr so ​​aus:

@Path("eventscheduler")
public class EventSchedulerService {
    private IEventSchedulerDao dao = new EventSchedulerDao();

    // in case a different implementation is to be used
    public void setEventSchedulerDao(IEventSchedulerDao dao) {
        this.dao = dao;
    }

    @Path("{uniqueName}")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Tournament getTournament(@PathParam("name") String uniqueName) {
        return dao.get(uniqueName);
    }

    @Path("create")
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Tournament createTournament(Tournament tournament) {
        return dao.create(tournament);
    }
}

Wenn das DAO ein Singleton wäre, aber ich denke, es würde keinen großen Unterschied geben, nur in der ersten Zeile:

private IEventSchedulerDao dao = EventSchedulerDao.getInstance();

Ich müsste immer noch eine IEventSchedulerDaoInstanz verwenden, aber ich denke, alle Singletons funktionieren so, oder? Aus irgendeinem Grund korreliere ich Singletons immer mit statischen Methoden. Anstatt also eine Singleton-Instanz für den Benutzer sichtbar zu machen getInstance(), wird diese ausgeblendet und er / sie verwendet nur EventSchedulerDao.get(name)usw. auf statische Weise. Ist das eine Sache oder bin das nur ich?

Soll ich also einzelne DAOs haben oder nicht?

Und als Nebenfrage, ist es in Ordnung für mich, offene Türen zu haben, damit der Benutzer seine eigenen DAOs implementieren kann?

Dabadaba
quelle
Sie können ein IoC-Singleton anstelle eines Singleton mit einem statischen Accessor verwenden.
CodesInChaos

Antworten:

10

Ich würde keinen Singleton verwenden. Es ist ein anerkanntes Anti-Pattern und erschwert das Testen. Ich würde viel lieber eine konkrete Implementierung einbinden und Ihrem Dienst eine DAO-Schnittstelle zuweisen (mit der Sie verschiedene Implementierungen einbinden können).

Brian Agnew
quelle
1
Was Sie in Ihrem letzten Satz vorschlagen, ist genau das, was ich richtig mache?
Dabadaba
1
Sie referenzieren über eine Schnittstelle (ja), aber Sie injizieren nicht das DAO (um klar zu sein ...)
Brian Agnew
Was meinen Sie? Ich habe einen Setter dafür, nicht wahr?
Dabadaba
@dabadaba Die Leitung private IEventSchedulerDao dao = new EventSchedulerDao();ist, wo Sie falsch gegangen sind. Die Implementierung für IEventSchedulerDaosollte über den Konstruktor injiziert und niemals geändert werden (dh auch loswerden setEventSchedulerDao).
David Arno
Okay ich verstehe. Ich habe das nur getan, um ein Standard-DAO bereitzustellen, und es zu ändern, wäre "optional". Aber Ihren Vorschlag anzunehmen bedeutet, einen Konstruktor für den Dienst zu haben, der sich vom Standard unterscheidet, und ehrlich gesagt habe ich keine Ahnung, wie das mit Jersey funktioniert, da der Standardkonstruktor verwendet wird. Weißt du zufällig, wie das geht?
Dabadaba
4

A D ata A ccess O bject sollte in Ihrer Anwendung nur dann wirklich einmal existieren. Die Logik bleibt die gleiche, die einzigen Unterschiede sind die Werte, die in die vom DAO bereitgestellten Methoden ein- und ausgehen.

In diesem Sinne ist es offensichtlich das erste, was normalerweise passiert, das DAO als starken Singleton zu implementieren. Das heißt, wenn Sie eine staticMethode für eine Factory-Klasse haben, die so etwas wie das getInstanceverzögerte Laden einer DAO-Instanz, wenn sie null ist, und das Zurückgeben derselben.

Entschuldigen Sie, wenn die Syntax nicht ganz stimmt, ich bin kein Java-Programmierer.

class DaoSingletonFactory
{
    private static Dao dao = null;

    public static Dao getInstance()
    {
        if (DaoSingletonFactory.dao == null) {
            DaoSingletonFactory.dao = new Dao();
        }

        return DaoSingletonFactory.dao;
    }
}

class UsesDao
{
    public void someMethod()
    {
        Dao dao = DaoSingletonFactory.getInstance();
    }
}

Dies ist unglaublich schwer zu testen, da Sie die Implementierung nicht austauschen können, ohne den Code der UsesDaoKlasse zu ändern . Dies kann durch das Patchen von Affen geschehen , wird jedoch im Allgemeinen nicht als gute Praxis angesehen.

Dann gibt es den besseren Weg, das schwache Singleton- Muster, bei dem Sie keine Instanz über eine staticMethode abrufen , sondern alle Klassen entweder über einen Konstruktor oder einen Setter von der Instanz abhängig machen (in Ihrem Fall verwenden EventSchedulerServiceSie die Setter-Injection).

Das einzige Problem ist, dass Sie dann sicherstellen müssen, dass alle Klassen, die von einer Instanz der Klasse abhängen, die nur einmal in Ihrem Anwendungslebenszyklus vorhanden sein sollte, dieselbe Instanz wie ihr Parameter verwenden, d. H. Das newwird nur einmal auf dem DAO-Objekt in der gesamten Anwendung aufgerufen.

Offensichtlich ist es unglaublich schwierig, dies zu verfolgen, und die Erstellung des Objektgraphen ist eine mühsame und lästige Arbeit.

Zum Glück gibt es IoC- Container, die es viel einfacher machen. Neben Spring ist der Guice IoC-Container von Google bei Java-Programmierern sehr beliebt.

Wenn Sie einen IoC-Container verwenden, konfigurieren Sie ihn so, dass er sich auf eine bestimmte Art und Weise verhält, z. Sie sagen, ob es bestimmte Klassen erstellen soll und ob eine Klasse als Abhängigkeit erforderlich ist, wie die Abhängigkeit aussehen soll (ob es sich immer um eine neue Instanz oder um einen Singleton handeln soll) und der Container verkabelt alles.

Sie können diesen Link für ein Singleton-Beispiel mit Guice überprüfen .


Vor- und Nachteile der Verwendung eines IoC-Containers

Vorteile

  • Sie sparen Budget, indem Sie nicht alle Factory-Methoden selbst schreiben müssen
  • (in der Regel) sehr unkomplizierte Konfiguration
  • schnelle Entwicklung

Nachteile

  • Als Zauberer sind die Klassen irgendwie aufgebaut und man kann nicht wirklich sehen, wie es passiert ist
  • Ein kleiner Leistungsabfall aufgrund der Klassensuche (manuell geschriebene Fabriken sind etwas schneller)
Andy
quelle
1

Singleton bezieht sich auf das Konzept nur auf eine Instanz und den Weg, um Zugriff auf die Instanz zu erhalten (durch die so berühmte statische Methode getInstance () )

Aber dahinter steckt noch eine Instanz. Ein gebautes Objekt mit einer Art eingeschränktem Zugriff.

In Ihrem Fall würde ich mich eher für den DI-Ansatz (Dependency Injection) entscheiden. Wie der erste Codeblock, den Sie verfügbar gemacht haben. Nur eine kleine Veränderung. Injizieren Sie das DAO über den Konstruktor. Zu entfernen oder nicht der Setter liegt bei Ihnen. Wenn Sie den Controller vor Änderungen in der Laufzeit schützen möchten, entfernen Sie ihn. Wenn Sie eine solche Möglichkeit anbieten wollen, dann behalten Sie sie.

Sie haben die Möglichkeit, ein Interface zu verwenden und ein offenes Fenster für weitere DAO-Implementierungen anzubieten. Kann oder kann nicht benötigt werden. Es dauert nur noch eine Minute und macht Ihr Design flexibel. Ihre im Gedächtnis DAO ist ziemlich verbreitet. Sehr nützlich als Mock zum Testen. Oder als Standard-DAO-Implementierung.

Nur ein Hinweis. Statische Ressourcen (Objekte, Methoden, Konstanten oder Variablen) sind wie globale Ressourcen. Ob Globales böse sind oder nicht, ist eine Frage der Bedürfnisse oder des Geschmacks. Es gibt jedoch implizite Mängel, die an sie gebunden sind. Diese beziehen sich auf Parallelität , Thread-Sicherheit (in Java keine Kenntnisse in anderen Sprachen), Serialisierung ...

Ich würde daher empfehlen, die Statik vorsichtig zu verwenden

Laiv
quelle