"Statisch" als semantischer Hinweis auf Staatenlosigkeit?

11

Ich habe kürzlich ein Refactoring eines mittelgroßen Projekts in Java durchgeführt, um zurück zu gehen und Unit-Tests hinzuzufügen. Als ich merkte, was für ein Schmerz es war, Singletons und Statik zu verspotten, "bekam" ich endlich, was ich die ganze Zeit über sie gelesen hatte. (Ich bin einer von denen, die aus Erfahrung lernen müssen. Na ja.)

Jetzt, da ich Spring verwende, um die Objekte zu erstellen und zu verkabeln, werden die staticSchlüsselwörter links und rechts entfernt. (Wenn ich mich möglicherweise darüber lustig machen könnte, ist es nicht wirklich statisch in dem Sinne, wie es Math.abs () ist, oder?) Die Sache ist, ich hatte mir angewöhnt, damit staticzu bezeichnen, dass sich eine Methode nicht darauf stützte auf jeden Objektzustand. Beispielsweise:

//Before
import com.thirdparty.ThirdPartyLibrary.Thingy;
public class ThirdPartyLibraryWrapper {
    public static Thingy newThingy(InputType input) {
         new Thingy.Builder().withInput(input).alwaysFrobnicate().build();
    }
}

//called as...
ThirdPartyLibraryWrapper.newThingy(input);
//After
public class ThirdPartyFactory {
    public Thingy newThingy(InputType input) {
         new Thingy.Builder().withInput(input).alwaysFrobnicate().build();
    }
}

//called as...
thirdPartyFactoryInstance.newThingy(input);

Also, hier wird es empfindlich. Ich mochte den alten Weg, weil mir der Großbuchstabe sagte, dass ThirdPartyLibraryWrapper.newThingy (x) genau wie Math.sin (x) jedes Mal das Gleiche tat. Es gibt keinen Objektstatus, um zu ändern, wie das Objekt das tut, was ich von ihm verlange. Hier sind einige mögliche Antworten, die ich in Betracht ziehe.

  • Niemand sonst fühlt sich so, also stimmt etwas nicht mit mir. Vielleicht habe ich die OO-Art, Dinge zu tun, einfach nicht wirklich verinnerlicht! Vielleicht schreibe ich in Java, denke aber in FORTRAN oder so. (Was beeindruckend wäre, da ich FORTRAN noch nie geschrieben habe.)
  • Vielleicht verwende ich Statik als eine Art Proxy für Unveränderlichkeit, um über Code nachzudenken. Abgesehen davon, welche Hinweise sollte ich in meinem Code haben, damit jemand mitkommt, um zu wissen, was zustandsbehaftet ist und was nicht?
  • Vielleicht sollte dies nur kostenlos sein, wenn ich gute Objektmetaphern wähle? zB thingyWrapperhört sich nicht so an, als hätte es einen Zustand unabhängig von der Verpackung, der Thingyselbst veränderlich sein kann. Ebenso thingyFactorysollte ein Sound unveränderlich sein, könnte aber unterschiedliche Strategien haben, die bei der Erstellung ausgewählt werden.
leoger
quelle

Antworten:

12

Ich mochte den alten Weg, weil mir der Großbuchstabe sagte, dass ThirdPartyLibraryWrapper.newThingy (x) genau wie Math.sin (x) jedes Mal das Gleiche tat. Es gibt keinen Objektstatus, um zu ändern, wie das Objekt das tut, was ich von ihm verlange.

Das ist der richtige Weg, ja. Das heißt, es gibt statickeine Garantie für Unveränderlichkeit oder staatliche Beharrlichkeit. Dass Sie es als Vorschlag für solche Garantien verwenden, ist zwar sinnvoll, aber keineswegs sicher.

Folgendes berücksichtigen:

public static class Logger
{
    public static int LogLevel { get; set; }

    public static void Log(string message, int logLevel)
    {
        if (logLevel >= LogLevel)
        {
            // logs the message, but only if it is important enough.
        }
    }
}

Diese Klasse enthält nicht nur den Status, sondern das Verhalten der Logger.Log()Methode ändert sich, wenn dieser Status geändert wird. Es ist eine absolut legitime Übung eines staticKlassenmusters, aber es hat keine der semantischen Garantien, die Sie vorschlagen.

Robert Harvey
quelle
Ich habe Ihnen das Häkchen gegeben, weil Sie einen nützlichen Kontrapunkt zur Grundlage meiner Intuition darstellen, aber ich möchte trotzdem Ihre Gedanken darüber bekommen, wie ich diesen Vertrag / diese Erwartung über mangelnden Zustand und fehlende Nebenwirkungen durch Codierung darstellen soll Konventionen.
Leoger
1
Normalerweise sind solche Erwartungen in der Dokumentation der Methode enthalten. Gleiches gilt für die Gewindesicherheit.
Robert Harvey
5

Meiner Meinung nach ist das, was Sie sagen - alle statischen Funktionen müssen nullipotent sein - in den meisten Fällen eine gute OO-Methode zum Schreiben von Code, aber ich glaube nicht, dass Sie beim Betrachten einer statischen Funktion automatisch davon ausgehen sollten, dass sie keine Nebenwirkungen hat . Die Situation kann komplexer sein; Nehmen Class.forName(String)wir zum Beispiel die Funktion, die zustandslos zu sein scheint, aber tatsächlich eine Klasse in den Speicher lädt und schließlich statische Felder instanziiert / statischen Initialisierer ausführt. Dies ist ein Beispiel für eine idempotente Funktion (eventuelle Aufrufe nach der ersten machen keinen Unterschied), aber es ist keine reine Funktion (es kann nicht gesagt werden, dass sie keine Nebenwirkungen hat). Dies ist auch eine gute Wahl, aber es kann Fälle geben, in denen drei unterschiedliche Aufrufe derselben Funktion drei unterschiedliche Ergebnisse liefern. Zum Beispiel, wenn Sie anrufenThread.currentThread() In einer Multithread-Anwendung gibt es keine Garantie dafür, dass Sie jedes Mal denselben Thread erhalten.

Abgesehen davon, welche Hinweise sollte ich in meinem Code haben, damit jemand mitkommt, um zu wissen, was zustandsbehaftet ist und was nicht?

Eine geeignete Lösung ist das Dokumentieren (z. B. Javadoc). Auch aus dem Namen der Funktion und aus dem, was sie manchmal tut, kann geschlossen werden, dass die statische Funktion eine reine Funktion ist. Zum Beispiel gibt es keinen Grund für jemanden zu glauben, dass Assert.assertTrue(boolean)JUnit irgendwo einen Zustand ändern würde. Wenn andererseits eine Funktion wie System.clearProperty(String)aufgerufen wird, ist es ziemlich offensichtlich, dass es Nebenwirkungen geben wird.

m3th0dman
quelle
Was ist der Unterschied zwischen "nullipotent" und "staatenlos"?
Pacerier
@ Pacerier Es sollte keinen Unterschied geben.
m3th0dman
4

Abgesehen davon, welche Hinweise sollte ich in meinem Code haben, damit jemand mitkommt, um zu wissen, was zustandsbehaftet ist und was nicht?

Sie können sie statisch machen. FxCop in der C # -Welt wird Sie dazu bringen, Dinge statisch zu machen, die keine Referenzmitgliedsvariablen enthalten. Dies ist (normalerweise) das Richtige.

Als ich merkte, was für ein Schmerz es war, Singletons und Statik zu verspotten, "bekam" ich endlich, was ich die ganze Zeit über sie gelesen hatte.

Es ist vielleicht nicht der richtige Ansatz, alle auf Instanzen zu verschieben. Entkoppeln Sie stattdessen die Abhängigkeiten von statischen Methoden von den Klassen, die sie verwenden. Sie können die statischen Methoden isoliert testen und dann die Abhängigkeit in den Verbrauchern ersetzen, wenn sie getestet werden müssen.

Telastyn
quelle
Können Sie mir ein Beispiel für die "Entkopplung der Abhängigkeiten von statischen Methoden" geben? Mir ist nicht klar, was Sie aktivieren. Ich habe die Implementierungsdetails bereits in einer statischen Methode ausgeblendet. Am Ende des Tages, die Klasse, die verwendet es braucht , um zu erreichen , dass die Funktionalität. Wollen Sie damit sagen, dass ich ein Instanzobjekt erstellen sollte, das Thin Wrapper für alle statischen Methoden enthält?
Leoger
1
@leoger - Die Klasse, die die statische Methode verwendet, muss die Funktionalität nicht erreichen, wenn Sie diese Klasse testen. Andernfalls würden Sie die statische Methode nicht verspotten.
Telastyn