Kann ich nicht einfach alle statischen Methoden anwenden?

64

Was ist der Unterschied zwischen den beiden folgenden UpdateSubject-Methoden? Ich fand es besser, statische Methoden zu verwenden, wenn Sie nur mit den Entitäten arbeiten möchten. In welchen Situationen sollte ich nicht statische Methoden anwenden?

public class Subject
{
    public int Id {get; set;}
    public string Name { get; set; }

    public static bool UpdateSubject(Subject subject)
    {
        //Do something and return result
        return true;
    }
    public bool UpdateSubject()
    {
        //Do something on 'this' and return result
        return true;
    }
}

Ich weiß, dass ich für diese wirklich nervige Frage viele Kicks von der Community bekommen werde, aber ich konnte mich nicht davon abhalten, sie zu stellen.

Wird dies beim Umgang mit Vererbung unpraktisch?

Update:
Es passiert jetzt bei uns am Arbeitsplatz. Wir arbeiten an einer 6-monatigen asp.net-Webanwendung mit 5 Entwicklern. Unser Architekt entschied, dass wir alle statischen Methoden für alle APIs verwenden. Seine Argumente für statische Methoden sind geringes Gewicht und es kommt Webanwendungen zugute, indem die Serverlast niedrig gehalten wird.

Alexander
quelle
9
Rich Hickey würde so etwas Nicht-Idiomatisches fördern (alle statischen Methoden). Wenn Sie einen funktionalen Stil mögen, können Sie auch eine funktionale Sprache verwenden;) Nicht alles in der Software lässt sich am besten als Objekt modellieren.
Job
13
@Job: Ich sehe nicht wirklich, wie das funktioniert - es ist prozedural.
Aaronaught
2
@ Job: Diese Klasse ist nicht unveränderlich.
Aaronaught
3
@DonalFellows: Natürlich können Sie, C # und F # sind beide gemischte Paradigmen. Aber die Tatsache bleibt, statische Methoden! = Funktionale Programmierung. FP bedeutet Übergabe- / Verkettungsfunktionen, unveränderliche Datentypen, Vermeidung von Nebenwirkungen usw. Das ist das Gegenteil von dem, was das Code-Snippet im OP tut.
Aaronaught
4
"Seine Argumentation als statische Methode ist leicht und es kommt Webanwendungen zugute, indem die Serverlast niedrig gehalten wird." Das ist falsch. Die Serverlast niedrig halten?
mamcx

Antworten:

87

Ich gehe mit den offensichtlichsten Problemen statischer Methoden mit expliziten "this" -Parametern um:

  1. Sie verlieren den virtuellen Versand und anschließend den Polymorphismus. Sie können diese Methode in einer abgeleiteten Klasse niemals überschreiben. Natürlich können Sie eine new( static) -Methode in einer abgeleiteten Klasse deklarieren , aber jeder Code, der darauf zugreift, muss die gesamte Klassenhierarchie kennen und explizite Überprüfungen und Umwandlungen durchführen, was genau das ist, was OO vermeiden soll .

  2. Bei einer Erweiterung von 1 können Sie Instanzen der Klasse nicht durch ein Interface ersetzen , da Interfaces (in den meisten Sprachen) keine staticMethoden deklarieren können.

  3. Die unnötige Ausführlichkeit. Was ist besser lesbar: Subject.Update(subject)oder einfach subject.Update()?

  4. Argumentüberprüfung. Auch dies hängt von der Sprache ab, aber viele kompilieren eine implizite Prüfung, um sicherzustellen, dass das thisArgument nicht dazu dient null, zu verhindern, dass ein Nullreferenzfehler unsichere Laufzeitbedingungen erzeugt (Art eines Pufferüberlaufs). Wenn Sie keine Instanzmethoden verwenden, müssen Sie diese Prüfung am Anfang jeder Methode explizit hinzufügen.

  5. Es ist verwirrend. Wenn ein normaler, vernünftiger Programmierer eine staticMethode sieht , geht er natürlich davon aus, dass keine gültige Instanz erforderlich ist (es sei denn, es werden mehrere Instanzen wie eine Vergleichs- oder Gleichheitsmethode benötigt oder es wird erwartet, dass sie nullReferenzen verarbeiten können). . Wenn wir sehen, wie statische Methoden auf diese Weise angewendet werden, müssen wir eine doppelte oder dreifache Aufnahme machen, und nach dem vierten oder fünften Mal werden wir gestresst und wütend sein und Gott helfe Ihnen, wenn wir Ihre Privatadresse kennen.

  6. Es ist eine Form der Vervielfältigung. Was beim Aufrufen einer Instanzmethode tatsächlich passiert, ist, dass der Compiler oder die Laufzeit die Methode in der Methodentabelle des Typs nachschlägt und sie thisals Argument aufruft . Grundsätzlich implementieren Sie neu, was der Compiler bereits tut. Sie verletzen DRY und wiederholen denselben Parameter in verschiedenen Methoden immer wieder, wenn er nicht benötigt wird.

Es ist schwer, sich einen guten Grund vorzustellen, Instanzmethoden durch statische Methoden zu ersetzen. Bitte nicht

Aaronaught
quelle
5
+1 für Ihre letzte Zeile allein. Ich wünschte, viel mehr Leute würden "Instanzmethode" denken, bevor sie "statische Methode" denken.
Stuart Leyland-Cole
4
+1. Ich würde hinzufügen, dass statische Methoden das Schreiben von Tests schwierig machen, da Sie sie in den meisten Fällen nicht verspotten können: Wenn ein Teil Ihres Codes von einer statischen Methode abhängt, können Sie ihn nicht durch eine no- ersetzen. op in Ihren Tests. Ein gutes Beispiel in der .NET - Welt wäre Klassen mögen File, FileInfound DirectoryInfo(und in der Tat gibt es Bibliotheken , die sie hinter Schnittstellen verstecken , die dann je nach Bedarf und in Tests verspottet aus injiziert werden können).
sm
Ich denke, Ihre Punkte zu Polymorphismus / Vererbung sind umstritten. Man sollte Vererbung nicht für die Wiederverwendung von Code verwenden, das ist also irrelevant. auch der erbteil ist zweifelhaft. In den meisten modernen Sprachen sind Methodensignaturen an sich polymorph. Wenn Sie einen funktionalen Ansatz wählen, beginnen Sie damit, Methoden weiterzugeben und stellen komplexe Methoden aus einfachen Methoden zusammen, die auf der Grundlage ihrer Schnittstelle austauschbar sind. Es gibt grundsätzlich keine Nachteile für statische Methoden, die nicht durch einfaches Entwerfen unter Berücksichtigung derselben wie bei OOP ausgeglichen werden.
Sara
@sm das stimmt einfach nicht. Wenn Sie eine harte Abhängigkeit von ALLEM haben, ob statisch oder nicht, was nicht in einem Komponententest enthalten ist, dann ist es nicht testbar, Punkt. statisch verursacht dies nicht. Eine statische Methode kann genauso injiziert werden wie eine Klasse, die eine Datenbankverbindung repräsentiert. Sie gehen davon aus, dass "statisch" "statisch plus fest verborgene Abhängigkeiten, die den globalen Status und externe Ressourcen berühren" bedeutet. das ist nicht fair.
Sara
@kai versuchen Sie, eine statische Methode zu injizieren, die Y anstelle von X ausführt, unabhängig davon, was X und Y sind. Sie müssen keine Abhängigkeiten von etwas Externem eingehen, das verschraubt werden soll. Vielleicht möchten Sie nur einen Aspekt testen, für den keine langwierige Berechnung X erforderlich ist. Wie können Sie dann Ihre ausgefallene statische Methode injizieren, ohne einen Wrapper zu erstellen? Das Weitergeben von Funktionen wird nicht von jeder Sprache unterstützt. Statische Methoden haben ihre Anwendungsfälle, und ich verwende sie, wenn dies sinnvoll ist . Dies ist, wie immer, ein Fall von "Nur weil du es kannst, heißt das nicht unbedingt, dass du es solltest".
sm
13

Sie haben ziemlich genau beschrieben, wie Sie OOP in C ausführen, in dem nur statische Methoden verfügbar sind, und "this" ist ein Strukturzeiger. Und ja, Sie können Laufzeitpolymorphie in C ausführen, indem Sie während der Konstruktion einen Funktionszeiger angeben, der als ein Element der Struktur gespeichert werden soll. In anderen Sprachen verfügbare Instanzmethoden sind nur syntaktischer Zucker, der im Grunde genau dasselbe unter der Haube tut. Einige Sprachen, wie z. B. Python, befinden sich auf halber Strecke, wobei der "self" -Parameter explizit aufgeführt ist. Eine spezielle Syntax für den Aufruf übernimmt jedoch die Vererbung und den Polymorphismus für Sie.

Karl Bielefeldt
quelle
FWIW, mit C können Sie virtuellen Versand wie folgt durchführen: obj->type->mthd(obj, ...);und das ist im Wesentlichen ähnlich wie beim virtuellen Versand in anderen Sprachen (aber hinter den Kulissen).
Donal Fellows
@ Karl Kannst du bitte eine Art schriftlichen Hinweis geben, um tiefer zu graben?
Jorge Lavín
Möglicherweise möchten Sie GObject als eine gute Referenzimplementierung ansehen .
Karl Bielefeldt
10

Das Problem mit der statischen Methode tritt in dem Moment auf, in dem Sie eine Unterklasse benötigen.

Statische Methoden können in Unterklassen nicht überschrieben werden. Daher können Ihre neuen Klassen keine neuen Implementierungen der Methoden bereitstellen, sodass sie weniger nützlich sind.


quelle
2
Dies ist nicht unbedingt ein Problem. Einige Sprachen bieten andere Mechanismen (nicht-virtuelle Methoden in C ++ und C #), um das gleiche absichtlich zu erreichen - C # bietet sogar "versiegelte" Methoden, um diese Idee weiter zu erweitern! Natürlich würden Sie das nicht einfach so tun, aber es ist eine gute Technik, sich dessen bewusst zu sein ...
Shog9
@Steve, das ersetzt nicht, da man beide Methoden immer noch direkt aufrufen kann.
@Steve, in Java können statische Methoden nicht überschrieben werden.
@ Thorbjørn - Erstens ist die Frage nicht mit Java oder einer anderen spezifischen OOP-Sprache markiert. Noch wichtiger ist, dass Sie keine statische Elementfunktion überschreiben können. Ich habe versucht, eine Analogie zu verwenden, um einen Punkt zu machen. Da ich eindeutig gescheitert bin, wurden Kommentare gelöscht.
Steve314
2
In gewisser Hinsicht handelt es sich hierbei immer noch um "neue Implementierungen von Methoden". Nur nicht im üblichen OOP-Sinne. Das fühlt sich ein bisschen so an, als würde ich sagen "Ihre Formulierung ist auch nicht perfekt, nur-nur-na-nur-nur" ;-)
Steve314
7

Wenn Sie eine Methode deklarieren static, müssen Sie kein Objekt aus der Klasse instanziieren (mithilfe des newSchlüsselworts), um die Methode auszuführen. Sie können jedoch nur dann auf Elementvariablen verweisen, wenn diese ebenfalls statisch sind. In diesem Fall gehören diese Elementvariablen zur Klasse und nicht zu einem bestimmten instanziierten Objekt der Klasse.

Anders ausgedrückt, das staticSchlüsselwort bewirkt , dass eine Deklaration Teil der Klassendefinition ist, sodass sie für alle Instanzen der Klasse (aus der Klasse instanziierte Objekte) zu einem einzigen Referenzpunkt wird .

Wenn Sie also mehrere laufende Kopien Ihrer Klasse möchten und nicht möchten, dass der Status (Elementvariablen) von allen laufenden Kopien gemeinsam genutzt wird (dh, jedes Objekt hat seinen eigenen Status), dann Sie Diese Mitgliedsvariablen können nicht als statisch deklariert werden.

Im Allgemeinen sollten Sie staticKlassen und Methoden nur verwenden, wenn Sie Dienstprogrammmethoden erstellen, die einen oder mehrere Parameter akzeptieren und ein Objekt zurückgeben, ohne dass Nebenwirkungen auftreten (dh Änderungen an Statusvariablen in der Klasse).

Robert Harvey
quelle
1
Wenn ich weiß, was OP staticbedeutet, fragt er, warum nicht einfach alles als statisch deklariert werden soll.
Ed S.
1
Er wird vorbei in einer Instanz Subject- aber als expliziter Parameter anstelle des impliziten thisParameter.
Steve314
Na ja ... aber ich verstehe deinen Standpunkt nicht, nehme ich an.
Ed S.
@Ed - nur, dass Sie mit einem expliziten Parameter so ziemlich alles machen können, wie mit einem impliziten thisParameter. Es gibt Unterschiede in den Erwartungen, aber außerhalb von inheritence, gibt es keinen wirklichen Unterschied in dem, was Sie können tun. Auf diese nicht statischen Elemente kann beispielsweise über den Parameter explicit zugegriffen werden.
Steve314
Ich ging davon aus, dass Sie nicht einmal mit einer statischen Methode, die in derselben Klasse deklariert wurde (wie Sie es in C ++ können), an private Mitglieder des Arguments in C # gelangen können. Ich bin mir nicht sicher, warum ich das gedacht habe, aber ich habe mich geirrt.
Ed S.
3

Der Grund, warum Leute argumentieren, dass "statische Methoden schlechtes Design zeigen", liegt nicht unbedingt an den Methoden, sondern an statischen Daten, implizit oder explizit. Implizit meine ich JEDEN Zustand im Programm, der nicht in den Rückgabewerten oder Parametern enthalten ist. Das Ändern des Zustands in einer statischen Funktion ist ein Rückfall in die prozedurale Programmierung. Wenn die Funktion den Status jedoch nicht ändert oder die Statusänderung an Komponentenobjektfunktionen delegiert, handelt es sich eher um ein Funktionsparadigma als um eine Prozedur.

Wenn Sie den Status nicht ändern, können statische Methoden und Klassen viele Vorteile haben. Dies erleichtert die Sicherstellung der Reinheit einer Methode und ist somit nebenwirkungsfrei und fehlerfrei und vollständig reproduzierbar. Es stellt außerdem sicher, dass unterschiedliche Methoden in mehreren Anwendungen, die eine gemeinsam genutzte Bibliothek verwenden, bei gleicher Eingabe dieselben Ergebnisse erzielen, was sowohl die Fehlerverfolgung als auch das Testen von Einheiten erheblich vereinfacht.

Letztendlich gibt es in der Praxis keinen Unterschied zwischen übergroßen Objekten wie StateManager und statischen oder globalen Daten. Der Vorteil von statischen Methoden ist, dass sie auf die Absicht des Autors hinweisen, den Status nicht für spätere Bearbeiter zu ändern.

Mathieson
quelle
1
Ihre Aussage "... ist ein Rückfall in die prozedurale Programmierung ..." lässt mich denken, dass Sie das so schlecht finden, ich denke, dass es in gewissem Sinne Teil von OO ist.
NoChance
making ... unit testing much easier.Nein, wird es nicht. Es macht es keinen Code macht , die statischen Methoden unmöglich nennt Einheit Test, da Sie es nicht aus der statischen Methode isolieren können. Ein Aufruf einer statischen Methode wird zu einer fest codierten Abhängigkeit von dieser Klasse.
StuperUser
2

Abhängig von der Sprache und dem, was Sie versuchen, kann die Antwort sein, dass es nicht viel Unterschied gibt, aber die staticVersion hat ein bisschen mehr Unordnung.

Da Ihre Beispielsyntax Java oder C # vorschlägt, halte ich Thorbjørn Ravn Andersen für richtig, um auf das vorrangige Problem (mit später Bindung) hinzuweisen. Bei einer statischen Methode gibt es keinen "speziellen" Parameter für die späte Bindung, sodass Sie keine späte Bindung haben können.

Tatsächlich ist eine statische Methode nur eine Funktion in einem Modul, das denselben Namen wie die Klasse hat - es ist überhaupt keine OOP-Methode.

Steve314
quelle
2

Du besiegst im Grunde den ganzen Punkt von Objekten, wenn du dies tust. Es gibt einen guten Grund, warum wir vom prozeduralen Code zum objektorientierten Code übergegangen sind.

Ich würde NIEMALS eine statische Methode deklarieren, die den Klassentyp als Parameter verwendet. Das macht den Code einfach komplexer, ohne Gewinn.

Loren Pechtel
quelle
3
Sie können dies tun, wenn Sie eine objectan eine staticMethode übergeben und eine neue zurückgeben object. Funktionale Programmierer tun dies die ganze Zeit.
Robert Harvey
frage: siehst du die Mathstatische klasse "complex for no gain" an oder würdest du es vorziehen, wenn alle zahlentypen virtuelle methoden hätten, die absoluten wert, negation, addition, quadrierung, beliebige wurzeln und so weiter definieren? Ich glaube nicht Objekte sind ordentlich, wenn Sie versteckte veränderbare Zustände speichern müssen, in denen Sie einige Invarianten erzwingen müssen. JEDE erdenkliche Methode, die auf einen Typ angewendet wird, in den Typ selbst zusammenzufassen, ist etwas, das zu komplexem und nicht wartbarem Code führt. Ich stimme Robert zu, vielleicht möchten Sie sich ansehen, wie funktionierende Programmierer arbeiten, es kann ein echter Augenöffner sein!
Sara
@kai Wie gesagt, so ziemlich. Die Math-Klasse ist eine vernünftige Ausnahme, obwohl es keine schlechte Idee wäre, sie an die numerischen Typen anzuhängen.
Loren Pechtel
2

Hier gibt es viele großartige Antworten, und sie sind viel aufschlussreicher und sachkundiger als alles, was ich jemals als Antwort finden könnte.

"Unser Architekt entschied, dass wir alle statischen Methoden für alle APIs verwenden. Seine Argumentation als statische Methoden ist leicht und es kommt Webanwendungen zugute, indem die Serverlast niedrig gehalten wird ."

(kühne Betonung liegt bei mir)

Mein 2c zu diesem Teil der Frage lautet:

Theoretisch ist das, was dort gesagt wird, wahr: Der Aufruf einer statischen Methode hat nur den Overhead, diese Methode tatsächlich aufzurufen. Das Aufrufen einer nicht statischen (Instanz-) Methode hat den zusätzlichen Aufwand, das Objekt zuerst zu instanziieren und zu einem bestimmten Zeitpunkt die Instanz zu zerstören (entweder manuell oder über irgendeine Form der automatischen Speicherbereinigung, abhängig von der verwendeten Plattform).

Das Spiel ein wenig von Devils Befürworter dazu: Wir können noch weiter gehen und Sachen sagen wie:

  • Dies kann sehr schlimm werden, wenn für jeden Aufruf der (Instanz-) Methode eine Instanz erstellt wird (anstatt sie einfach statisch zu lassen und so aufzurufen).

  • Abhängig von der Komplexität des Konstruktors, der Typhyerarchie, anderen Instanzmitgliedern und anderen solchen unbekannten Faktoren kann der zusätzliche Overhead des nicht statischen Methodenaufrufs variieren und sehr groß werden

In Wahrheit, IMHO, sind die obigen Punkte (und der allgemeine Ansatz von "Verwenden wir Statik, weil sie schneller ist") Strohmann-Argumente / Einschätzungen:

  • Wenn der Code gut ist und speziell für dieses Argument, wenn die Instanzen nur bei Bedarf erstellt (und in optimaler Weise zerstört) werden, entsteht kein zusätzlicher Aufwand durch die Verwendung von Insance-Methoden, wenn dies angezeigt ist (wenn Sie dies möchten) erstelle nur die benötigten Objekte, diese wären auch dann erstellt worden, wenn diese Methode als statisch deklariert worden wäre.

  • Wenn Sie die statische Methodendeklaration auf diese Weise missbrauchen, können Sie einige Probleme mit Ihrem Code in Bezug auf die Erstellung von Instanzen verbergen.

Shivan Drache
quelle
2

Dies ist eine sehr interessante Frage, die in der Regel sehr unterschiedliche Antworten hat, je nachdem, welche Community Sie sie stellen. Andere Antworten scheinen C # oder Java Bias zu sein: eine Programmiersprache, die (meistens) objektorientiert ist und eine Art idiomatische Sicht auf die Objektorientierung hat.

In anderen Communities wie C ++ wird die Objektorientierung freier interpretiert. Einige bekannte Experten haben sich mit dem Thema befasst und kommen zu dem Schluss, dass freie Funktionen die Kapselung verbessern (der Schwerpunkt liegt bei mir):

Wir haben jetzt gesehen, dass ein vernünftiger Weg, den Umfang der Kapselung in einer Klasse zu messen, darin besteht, die Anzahl der Funktionen zu zählen, die möglicherweise unterbrochen werden, wenn sich die Implementierung der Klasse ändert. In diesem Fall wird deutlich, dass eine Klasse mit n Mitgliedsfunktionen stärker gekapselt ist als eine Klasse mit n + 1 Mitgliedsfunktionen. Und diese Beobachtung rechtfertigt mein Argument , Funktionen, die nicht Mitglieder sind, gegenüber Funktionen, die Mitglieder sind, zu bevorzugen

Scott Meyers

Für diejenigen, die mit der C ++ - Terminologie nicht vertraut sind:

  • Mitgliedsfunktion = Methode
  • Nicht-Member-Funktion = statische Methode
  • non-friend = benutze nur die öffentliche API
  • non-member non-friend function = statische Methode, die nur die öffentliche API verwendet

Um Ihre Frage zu beantworten:

Kann ich nicht einfach alle statischen Methoden anwenden?

Nein, Sie sollten nicht immer statische Methoden verwenden.

Sie sollten sie jedoch immer dann verwenden, wenn Sie nur die öffentliche API einer Klasse verwenden müssen.

authchir
quelle
-3

in Java ...

Statische Methoden werden nicht überschrieben, sondern ausgeblendet. Dies wäre ein großer Unterschied (Problem?) Bei der Erweiterung der Klasse.

Andererseits ist mir aufgefallen, dass Java-Programmierer die Tendenz haben zu denken, alles MUSS objektiv sein, ohne wirklich zu verstehen, warum, und ich bin anderer Meinung.

Statisch sollte für den Code verwendet werden, der für die Klasse spezifisch ist. Statisch funktioniert nicht gut mit Vererbung.

Einige gute Beispiele für die Verwendung statischer Methoden sind eigenständige Funktionen ohne Nebenwirkungen.

siehe auch das Stichwort synchronisiert ...

Nicolas
quelle
2
Wie verbirgt sich diese statische Methode und wo würde dieser Unterschied und dieses Problem bei der Erweiterung der Klasse auftreten? Wie passt synchronisiert dazu? Wo treten die Probleme der Statik und Vererbung auf? Können Sie einige gute und schlechte statische Methoden in den Java-Kernbibliotheken bereitstellen und erklären, warum diese jeweils der Fall sind?