Ich arbeite gerade an einem größeren Soloprojekt und habe mehrere Klassen, in denen ich keinen Grund sehe, eine Instanz von zu erstellen.
Meine aktuelle Würfelklasse speichert beispielsweise alle ihre Daten statisch und alle ihre Methoden sind auch statisch. Ich muss es nicht initialisieren, denn wenn ich würfeln und einen neuen Wert erhalten möchte, benutze ich einfach Dice.roll()
.
Ich habe mehrere ähnliche Klassen, die nur eine Hauptfunktion wie diese haben, und ich beginne mit der Arbeit an einer Art "Controller" -Klasse, die für alle Ereignisse zuständig ist (z. B. wann sich ein Spieler bewegt und welcher Zug gerade ist) es ist) und ich habe festgestellt, dass ich die gleiche Idee für diese Klasse folgen könnte. Ich habe nie vor, mehrere Objekte für diese spezifischen Klassen zu erstellen. Wäre es also eine schlechte Idee, sie vollständig statisch zu machen?
Ich habe mich gefragt, ob dies in Bezug auf Java als "schlechte Praxis" angesehen wird. Nach allem, was ich gesehen habe, scheint die Community in Bezug auf dieses Thema gespalten zu sein. Jedenfalls würde ich gerne darüber diskutieren und Links zu Ressourcen wären auch großartig!
Antworten:
An statischen Klassen, die wirklich statisch sind, ist nichts auszusetzen . Das heißt, es gibt keinen internen Zustand, von dem die Ausgabe der Methoden sich ändern würde.
Wenn
Dice.roll()
einfach eine neue Zufallszahl von 1 bis 6 zurückgegeben wird, ändert sich der Status nicht. Zugegeben, Sie teilen möglicherweise eineRandom
Instanz, aber ich würde nicht in Betracht ziehen, dass bei einer Statusänderung per Definition die Ausgabe immer gut und zufällig sein wird. Es ist auch threadsicher, so dass es hier keine Probleme gibt.Es werden häufig endgültige "Helper" - oder andere Dienstprogrammklassen angezeigt, die einen privaten Konstruktor und statische Member haben. Der private Konstruktor enthält keine Logik und dient nur dazu, zu verhindern, dass jemand die Klasse instanziiert. Der letzte Modifikator bringt nur die Idee auf den Punkt, dass dies keine Klasse ist, von der Sie jemals abgeleitet werden möchten. Es ist nur eine Gebrauchsklasse. Bei ordnungsgemäßer Ausführung sollten keine Singleton- oder anderen Klassenmitglieder vorhanden sein, die selbst nicht statisch und endgültig sind.
Solange Sie diesen Richtlinien folgen und keine Singletons machen, ist daran absolut nichts auszusetzen. Sie erwähnen eine Controller-Klasse, und dies erfordert mit ziemlicher Sicherheit Statusänderungen. Ich rate daher davon ab, nur statische Methoden zu verwenden. Sie können sich stark auf eine statische Dienstprogrammklasse verlassen, sie jedoch nicht zu einer statischen Dienstprogrammklasse machen.
Was ist eine Zustandsänderung für eine Klasse? Lassen Sie uns Zufallszahlen für eine Sekunde ausschließen, da sie per Definition nicht deterministisch sind und sich daher der Rückgabewert häufig ändert.
Eine reine Funktion ist eine deterministische, dh für eine bestimmte Eingabe erhalten Sie genau eine Ausgabe. Sie möchten, dass statische Methoden reine Funktionen sind. In Java gibt es Möglichkeiten, das Verhalten statischer Methoden zu optimieren, um den Status zu halten, aber es sind fast nie gute Ideen. Wenn Sie eine Methode als statisch deklarieren , geht der typische Programmierer von vornherein davon aus, dass es sich um eine reine Funktion handelt. Abweichend vom erwarteten Verhalten neigen Sie generell dazu, Fehler in Ihrem Programm zu verursachen und sollten vermieden werden.
Ein Singleton ist eine Klasse, die statische Methoden enthält, die der "reinen Funktion" so entgegengesetzt sind, wie Sie nur können. Ein einzelnes statisches privates Mitglied wird intern in der Klasse gespeichert, um sicherzustellen, dass genau eine Instanz vorhanden ist. Dies ist keine bewährte Methode und kann Sie aus verschiedenen Gründen später in Schwierigkeiten bringen. Um zu wissen, wovon wir sprechen, hier ein einfaches Beispiel für einen Singleton:
quelle
Dice.roll()
ist keine gültige Ausnahme von der Regel "kein globaler Status".random.nextInt(6) + 1
. ;)RollAndMove()
erneut gewürfelt wird, wenn wir eine Doppelsechs liefern, dann ist die einfachste und robusteste Möglichkeit, dies zu tun, entwederDice
den Zufallszahlengenerator auszuspielen. Ergo,Dice
möchte keine statische Klasse sein, die einen statischen Zufallsgenerator verwendet.Um ein Beispiel für die Einschränkungen einer
static
Klasse zu geben, was ist, wenn einige Ihrer Spieler einen leichten Bonus auf ihre Würfelwürfe erhalten möchten? Und sie sind bereit, viel Geld zu bezahlen! :-)Ja, Sie können einen weiteren Parameter hinzufügen
Dice.roll(bonus)
.Später brauchst du D20s.
Dice.roll(bonus, sides)
Ja, aber einige Spieler haben die "überragende Leistung", so dass sie niemals "fummeln" können (1 würfeln).
Dice.roll(bonus, sides, isFumbleAllowed)
.Das wird unordentlich, nicht wahr?
quelle
new Dice().roll(...)
Muss ebenso wie statischer Zugriff berücksichtigt werdenDice.roll(...)
. Wir profitieren nur, wenn wir diese Abhängigkeit injizieren .static
bei jedem Anruf Unordnung auftritt und dass bei jedem Anruf das erforderliche Wissen erforderlich ist, damit die gesamte Anwendung global mit Unordnung überschwemmt wird . In einer OOP-Struktur muss nur der Konstruktor / die Factory chaotisch sein, und die Details dieses Chips sind gekapselt. Verwenden Sie danach Polymorphismus und rufen Sie einfach anroll()
. Ich könnte diese Antwort zur Klarstellung bearbeiten.Im speziellen Fall einer Dice-Klasse wird das Testen meiner Meinung nach durch die Verwendung von Instanzmethoden anstelle von statischen Methoden erheblich vereinfacht.
Wenn Sie einen Komponententest durchführen möchten, bei dem eine Instanz von Dice verwendet wird (z. B. eine Spielklasse), können Sie aus Ihren Tests eine Art Test-Double von Dice injizieren, das immer eine feste Folge von Werten zurückgibt. Ihr Test kann überprüfen, ob das Spiel das richtige Ergebnis für diese Würfelwürfe hat.
Ich bin kein Java-Entwickler, aber ich denke, es wäre bedeutend schwieriger, dies mit einer vollständig statischen Dice-Klasse zu tun. Siehe /programming/4482315/why-does-mockito-not-mock-static-methods
quelle
Dies ist eigentlich als das Monostate-Muster bekannt , bei dem jede Instanz (und sogar eine "Nicht-Instanz") ihren Status teilt. Jedes Mitglied ist ein Klassenmitglied (dh kein Instanzmitglied). Sie werden häufig verwendet, um "Toolkit" -Klassen zu implementieren, die eine Reihe von Methoden und Konstanten bündeln, die sich auf eine einzelne Verantwortung oder Anforderung beziehen, aber keinen Status benötigen, um zu funktionieren (sie sind rein funktional). In der Tat wird Java mit einigen davon gebündelt geliefert (zum Beispiel Math ).
Ein bisschen off-topic: ich mit der Benennung von Schlüsselwort in VisualBasic- selten zustimmen, aber in diesem Fall, ich denke ,
shared
ist sicherlich klarer und besser semantisch (es wird geteilt als in der Klasse selbst und alle seine Instanzen)static
(bleibt nach dem Lebenszyklus des Geltungsbereichs, in dem es deklariert ist).quelle