it-swarm.com.de

Statische Fabrik gegen Fabrik als Singleton

In einigen meiner Codes habe ich eine ähnliche statische Factory:

public class SomeFactory {

    // Static class
    private SomeFactory() {...}

    public static Foo createFoo() {...}

    public static Foo createFooerFoo() {...}
}

Während einer Codeüberprüfung wurde vorgeschlagen, dass dies ein Singleton sein und injiziert werden sollte. Es sollte also so aussehen:

public class SomeFactory {

    public SomeFactory() {}

    public Foo createFoo() {...}

    public Foo createFooerFoo() {...}
}

Ein paar Dinge hervorzuheben:

  • Beide Fabriken sind staatenlos.
  • Der einzige Unterschied zwischen den Methoden besteht in ihren Bereichen (Instanz vs. statisch). Die Implementierungen sind die gleichen.
  • Foo ist eine Bohne ohne Schnittstelle.

Die Argumente, die ich hatte, um statisch zu werden, waren:

  • Die Klasse ist zustandslos und muss daher nicht instanziiert werden
  • Es erscheint natürlicher, eine statische Methode aufrufen zu können, als eine Fabrik instanziieren zu müssen

Die Argumente für die Fabrik als Singleton waren:

  • Es ist gut, alles zu injizieren
  • Trotz der Staatenlosigkeit der Fabrik ist das Testen mit der Injektion einfacher (leicht zu verspotten)
  • Es sollte verspottet werden, wenn der Verbraucher getestet wird

Ich habe einige schwerwiegende Probleme mit dem Singleton-Ansatz, da dies darauf hindeutet, dass keine Methode jemals statisch sein sollte. Es scheint auch nahezulegen, dass Dienstprogramme wie StringUtils verpackt und injiziert werden sollten, was albern erscheint. Schließlich bedeutet dies, dass ich mich irgendwann über die Fabrik lustig machen muss, was nicht richtig erscheint. Ich kann mir nicht vorstellen, wann ich die Fabrik verspotten müsste.

Was denkt die Community? Obwohl ich den Singleton-Ansatz nicht mag, habe ich anscheinend kein besonders starkes Argument dagegen.

25
bstempi

Warum sollten Sie Ihre Fabrik von dem Objekttyp trennen, den sie erstellt?

public class Foo {

    // Private constructor
    private Foo(...) {...}

    public static Foo of(...) { return new Foo(...); }
}

Dies beschreibt Joshua Bloch als seinen Punkt 1 auf Seite 5 seines Buches "Effective Java". Java ist ausführlich genug, ohne zusätzliche Klassen und Singletons hinzuzufügen und so weiter. Es gibt einige Fälle, in denen eine separate Factory-Klasse sinnvoll ist, aber es gibt nur wenige.

Um Joshua Blochs Punkt 1 zu paraphrasieren, können statische Factory-Methoden im Gegensatz zu Konstruktoren:

  • Haben Sie Namen, die bestimmte Arten der Objekterstellung beschreiben können (Bloch verwendet als Beispiel probablePrime (int, int, Random)).
  • Gibt ein vorhandenes Objekt zurück (denken Sie: Fliegengewicht)
  • Gibt einen Untertyp der ursprünglichen Klasse oder eine von ihr implementierte Schnittstelle zurück
  • Reduzieren Sie die Ausführlichkeit (der Angabe von Typparametern - siehe zum Beispiel Bloch)

Nachteile:

  • Klassen ohne öffentlichen oder geschützten Konstruktor können nicht in Unterklassen unterteilt werden (Sie können jedoch geschützte Konstruktoren und/oder Punkt 16 verwenden: Komposition gegenüber Vererbung bevorzugen).
  • Statische Factory-Methoden heben sich nicht von anderen statischen Methoden ab (verwenden Sie einen Standardnamen wie () oder valueOf ()).

Wirklich, Sie sollten Blochs Buch zu Ihrem Code-Reviewer bringen und sehen, ob Sie beide Blochs Stil zustimmen können.

15
GlenPeterson

Hier sind einige der Probleme mit der Statik in Java:

  • statische Methoden spielen nicht nach den OOP "Regeln". Sie können eine Klasse nicht zwingen, bestimmte statische Methoden zu implementieren (soweit ich weiß). Sie können also nicht mehrere Klassen haben Implementieren des gleichen Satzes statischer Methoden mit den gleichen Signaturen. Sie bleiben also bei eins von allem, was Sie tun. Sie können eine statische Klasse auch nicht wirklich unterordnen. Also kein Polymorphismus , usw.

  • da Methoden keine Objekte sind, können statische Methoden nicht weitergegeben und nicht verwendet werden (ohne eine Menge Boilerplate-Codierung), außer durch Code, der fest mit ihnen verknüpft ist - - wodurch der Client-Code stark gekoppelt ist. (Um fair zu sein, ist dies nicht nur die Schuld der Statik).

  • statikfelder sind Globalen ziemlich ähnlich


Du erwähntest:

Ich habe einige schwerwiegende Probleme mit dem Singleton-Ansatz, da dies darauf hindeutet, dass keine Methode jemals statisch sein sollte. Es scheint auch nahezulegen, dass Dienstprogramme wie StringUtils verpackt und injiziert werden sollten, was albern erscheint. Schließlich bedeutet dies, dass ich mich irgendwann über die Fabrik lustig machen muss, was nicht richtig erscheint. Ich kann mir nicht vorstellen, wann ich die Fabrik verspotten müsste.

Was mich neugierig macht, Sie zu fragen:

  • was sind Ihrer Meinung nach die Vorteile statischer Methoden?
  • glauben Sie, dass StringUtils korrekt entworfen und implementiert ist?
  • warum denkst du, wirst du die Fabrik niemals verspotten müssen?
5
user39685

Ich denke, die Anwendung, die Sie erstellen, muss ziemlich unkompliziert sein, sonst befinden Sie sich relativ früh in ihrem Lebenszyklus. Das einzige andere Szenario, an das ich denken kann, wo Sie nicht bereits auf Probleme mit Ihrer Statik gestoßen wären, ist, wenn Sie es geschafft haben, die anderen Teile Ihres Codes, die über Ihre Factory Bescheid wissen, auf ein oder zwei Stellen zu beschränken.

Stellen Sie sich vor, Ihre Anwendung entwickelt sich weiter und in einigen Fällen müssen Sie ein FooBar (Unterklasse von Foo) erstellen. Jetzt müssen Sie alle Stellen in Ihrem Code durchgehen, die über Ihre SomeFactory Bescheid wissen, und eine Art Logik schreiben, die someCondition betrachtet und SomeFactory oder SomeOtherFactory aufruft. Wenn Sie sich Ihren Beispielcode ansehen, ist es tatsächlich schlimmer. Sie planen, nur Methode für Methode zum Erstellen verschiedener Foos hinzuzufügen, und Ihr gesamter Client-Code muss eine Bedingung überprüfen, bevor Sie herausfinden, welche Foo erstellt werden soll. Dies bedeutet, dass alle Kunden genau wissen müssen, wie Ihre Fabrik funktioniert, und dass sie sich alle ändern müssen, wenn ein neues Foo benötigt (oder entfernt!) Wird.

Stellen Sie sich nun vor, Sie hätten gerade ein IFooFactory erstellt, das ein IFoo ergibt. Das Erstellen einer anderen Unterklasse oder sogar einer völlig anderen Implementierung ist ein Kinderspiel - Sie geben einfach die richtige IFooFactory weiter oben in der Kette ein, und wenn der Client sie erhält, ruft er gimmeAFoo() auf und erhält die richtige foo weil es die richtige Fabrik hat.

Sie fragen sich vielleicht, wie sich dies im Produktionscode auswirkt. Um Ihnen ein Beispiel aus der Codebasis zu geben, an der ich arbeite und die sich nach etwas mehr als einem Jahr des Auslösens von Projekten allmählich abflacht. Ich habe eine Reihe von Fabriken, die zum Erstellen von Fragendatenobjekten verwendet werden (sie sind so etwas wie - Präsentationsmodelle ). Ich habe ein QuestionFactoryMap, das im Grunde ein Hash ist, um nachzuschlagen, welche Fragenfabrik wann verwendet werden soll. Um eine Anwendung zu erstellen, die unterschiedliche Fragen stellt, habe ich verschiedene Fabriken in die Karte eingefügt.

Fragen können entweder in Anweisungen oder in Übungen (die beide IContentBlock implementieren) gestellt werden, sodass jeder InstructionsFactory oder ExerciseFactory eine Kopie der QuestionFactoryMap erhält. Dann werden die verschiedenen Anweisungen und Übungsfabriken an die ContentBlockBuilder übergeben, die wiederum einen Hash verwaltet, der wann verwendet werden kann. Wenn das XML geladen wird, ruft der ContentBlockBuilder die Exercise- oder Instructions-Factory aus dem Hash heraus auf und fordert sie auf, createContent() zu verwenden. Anschließend delegiert die Factory die Erstellung der Fragen an das, was sie herausholt der internen QuestionFactoryMap basierend auf dem Inhalt jedes XML-Knotens im Vergleich zum Hash. Leider , dieses Ding ist ein BaseQuestion anstelle eines IQuestion, aber das zeigt nur, dass Sie selbst dann Fehler machen können, wenn Sie vorsichtig mit dem Design sind Sie auf der ganzen Linie.

Dein Bauch sagt dir, dass diese Statik dich verletzen wird, und es gibt sicherlich viel da draußen in der Literatur, um deinen Darm zu unterstützen. Sie werden niemals verlieren, wenn Sie eine Abhängigkeitsinjektion haben, die Sie nur für Ihre Komponententests verwenden (nicht, dass ich glaube, wenn Sie guten Code erstellen, werden Sie ihn nicht mehr verwenden), aber die Statik kann Ihre Fähigkeiten vollständig zerstören um Ihren Code schnell und einfach zu ändern. Warum also überhaupt dorthin gehen?

0
Amy Blankenship