Ich verstehe das Konzept eines Objekts und als Java-Programmierer habe ich das Gefühl, dass das OO-Paradigma in der Praxis für mich ganz natürlich ist.
Vor kurzem dachte ich jedoch:
Moment mal, was sind die praktischen Vorteile der Verwendung eines Objekts gegenüber der Verwendung einer statischen Klasse (mit ordnungsgemäßer Kapselung und OO-Praktiken)?
Ich könnte mir zwei Vorteile der Verwendung eines Objekts vorstellen (beide sind bedeutsam und leistungsstark):
Polymorphismus: Ermöglicht den dynamischen und flexiblen Austausch von Funktionen zur Laufzeit. Ermöglicht auch das einfache Hinzufügen neuer Funktionsteile und Alternativen zum System. Wenn beispielsweise eine
Car
Klasse für die Arbeit mitEngine
Objekten entwickelt wurde und Sie dem System, das das Auto verwenden kann, eine neue Engine hinzufügen möchten, können Sie eine neueEngine
Unterklasse erstellen und einfach ein Objekt dieser Klasse an dasCar
Objekt übergeben, ohne dies zu müssen etwas ändernCar
. Und Sie können sich zur Laufzeit dafür entscheiden.In der Lage sein, Funktionen weiterzugeben: Sie können ein Objekt dynamisch im System weitergeben.
Aber gibt es weitere Vorteile für Objekte gegenüber statischen Klassen?
Wenn ich einem System neue 'Teile' hinzufüge, erstelle ich häufig eine neue Klasse und instanziiere Objekte daraus.
Aber als ich kürzlich innehielt und darüber nachdachte, wurde mir klar, dass eine statische Klasse an vielen Stellen, an denen ich normalerweise ein Objekt verwende, genauso funktioniert wie ein Objekt.
Zum Beispiel arbeite ich daran, meiner App einen Mechanismus zum Speichern / Laden von Dateien hinzuzufügen.
Bei einem Objekt sieht die aufrufende Codezeile folgendermaßen aus: Thing thing = fileLoader.load(file);
Mit einer statischen Klasse würde es so aussehen: Thing thing = FileLoader.load(file);
Was ist der Unterschied?
Ziemlich oft fällt mir einfach kein Grund ein, ein Objekt zu instanziieren, wenn eine einfache alte statische Klasse genauso handeln würde. In OO-Systemen sind statische Klassen jedoch ziemlich selten. Also muss mir etwas fehlen.
Gibt es weitere Vorteile für andere Objekte als die beiden, die ich aufgelistet habe? Bitte erkläre.
EDIT: Zur Verdeutlichung. Ich finde Objekte sehr nützlich, wenn ich Funktionen austausche oder Daten weitergebe. Zum Beispiel habe ich eine App geschrieben, die Melodien enthält. MelodyGenerator
hatte mehrere Unterklassen, die Melodien unterschiedlich erzeugen, und Objekte dieser Klassen waren austauschbar (Strategiemuster).
Die Melodien waren auch Objekte, da es nützlich ist, sie weiterzugeben. So waren die Akkorde und Skalen.
Aber was ist mit "statischen" Teilen des Systems - die nicht weitergegeben werden? Zum Beispiel - ein Mechanismus zum Speichern von Dateien. Warum sollte ich es in einem Objekt implementieren und nicht in einer statischen Klasse?
quelle
Thing
?FileLoader
gegen eine austauschen müssen, die aus einem Socket liest? Oder ein Mock zum Testen? Oder eine, die eine Zip-Datei öffnet?System.Math
in .NET ist ein Beispiel für etwas, das als statische Klasse viel sinnvoller ist: Sie müssen es nie austauschen oder verspotten, und keiner der Vorgänge kann logischerweise Teil einer Instanz sein. Ich glaube wirklich nicht, dass Ihr "Spar" -Beispiel zu dieser Rechnung passt.Antworten:
lol du klingst wie ein Team, an dem ich gearbeitet habe;)
Java (und wahrscheinlich C #) unterstützt diesen Programmierstil auf jeden Fall. Und ich arbeite mit Leuten, deren erster Instinkt lautet: "Ich kann das zu einer statischen Methode machen!" Aber es gibt einige subtile Kosten, die Sie im Laufe der Zeit einholen.
1) Java ist eine objektorientierte Sprache. Und es macht die funktionalen Jungs verrückt, aber es hält wirklich ziemlich gut. Die Idee hinter OO besteht darin, Funktionalität mit Status zu bündeln, um kleine Daten- und Funktionseinheiten zu erhalten, die ihre Semantik beibehalten, indem der Status ausgeblendet und nur die Funktionen angezeigt werden, die in diesem Kontext sinnvoll sind.
Wenn Sie zu einer Klasse mit nur statischen Methoden wechseln, brechen Sie den "Zustand" -Teil der Gleichung. Aber der Staat muss noch irgendwo leben. Was ich im Laufe der Zeit gesehen habe, ist, dass eine Klasse mit allen statischen Methoden immer kompliziertere Parameterlisten hat, weil sich der Status von der Klasse in die Funktionsaufrufe bewegt.
Nachdem Sie eine Klasse mit allen statischen Methoden erstellt haben, führen Sie sie durch und untersuchen Sie einfach, wie viele dieser Methoden einen einzigen gemeinsamen Parameter haben. Es ist ein Hinweis darauf, dass dieser Parameter entweder die enthaltende Klasse für diese Funktionen sein sollte oder dass dieser Parameter ein Attribut einer Instanz sein sollte.
2) Die Regeln für OO sind ziemlich gut verstanden. Nach einer Weile können Sie sich ein Klassendesign ansehen und feststellen, ob es Kriterien wie SOLID erfüllt. Und nach vielen Tests mit Übungseinheiten entwickeln Sie ein gutes Gefühl dafür, was eine Klasse "richtig groß" und "kohärent" macht. Aber es gibt keine guten Regeln für eine Klasse mit allen statischen Methoden und keinen wirklichen Grund, warum Sie nicht einfach alles darin bündeln sollten. Die Klasse ist in Ihrem Editor geöffnet. Was zum Teufel? Fügen Sie dort einfach Ihre neue Methode hinzu. Nach einer Weile verwandelt sich Ihre Anwendung in eine Reihe konkurrierender "Gottobjekte", von denen jedes versucht, die Welt zu dominieren. Auch hier ist die Umgestaltung in kleinere Einheiten sehr subjektiv und schwer zu sagen, ob Sie es richtig verstanden haben.
3) Schnittstellen sind eine der leistungsstärksten Funktionen von Java. Die Klassenvererbung hat sich als problematisch erwiesen, aber das Programmieren mit Schnittstellen bleibt einer der mächtigsten Tricks der Sprache. (dito C #) All-statische Klassen können sich nicht in dieses Modell einfügen.
4) Es schlägt die Tür zu wichtigen OO-Techniken zu, die Sie nicht nutzen können. Sie können also jahrelang nur mit einem Hammer in Ihrem Werkzeugkasten arbeiten, ohne zu wissen, wie viel einfacher es gewesen wäre, wenn Sie auch einen Schraubenzieher gehabt hätten.
4.5) Es werden die schwierigsten und unzerbrechlichsten Abhängigkeiten zur Kompilierungszeit erstellt. Wenn Sie dies beispielsweise getan haben,
FileSystem.saveFile()
gibt es keine Möglichkeit, dies zu ändern, außer Ihre JVM zur Laufzeit vorzutäuschen. Dies bedeutet, dass jede Klasse, die auf Ihre statische Funktionsklasse verweist, eine harte Abhängigkeit zur Kompilierungszeit von dieser spezifischen Implementierung hat, was eine Erweiterung fast unmöglich macht und das Testen enorm erschwert. Sie können die statische Klasse isoliert testen, es wird jedoch sehr schwierig, die Klassen, die auf diese Klasse verweisen, isoliert zu testen.5) Sie werden Ihre Mitarbeiter verrückt machen. Die meisten Fachleute, mit denen ich zusammenarbeite, nehmen ihren Code ernst und achten zumindest auf einige Designprinzipien. Wenn sie die Kernabsicht einer Sprache beiseite lassen, ziehen sie sich die Haare aus, weil sie den Code ständig überarbeiten.
Wenn ich in einer Sprache bin, versuche ich immer, eine Sprache gut zu verwenden. Wenn ich zum Beispiel in Java bin, verwende ich gutes OO-Design, weil ich dann die Sprache wirklich für das nutze, was sie tut. Wenn ich in Python bin, mische ich Funktionen auf Modulebene mit gelegentlichen Klassen - ich konnte nur Klassen in Python schreiben, aber dann würde ich die Sprache nicht für das verwenden, was sie kann.
Eine andere Taktik besteht darin, eine Sprache schlecht zu benutzen, und sie beschweren sich über alle Probleme, die sie verursacht. Das gilt aber für so ziemlich jede Technologie.
Das Hauptmerkmal von Java ist die Verwaltung der Komplexität in kleinen, testbaren Einheiten, die zusammen hängen, damit sie leicht zu verstehen sind. Java betont klare Schnittstellendefinitionen unabhängig von der Implementierung - was ein großer Vorteil ist. Deshalb wird es (und andere ähnliche OO-Sprachen) weiterhin so häufig verwendet. Bei aller Ausführlichkeit und Ritualität habe ich immer das Gefühl, dass die Ideen im Code sauberer getrennt sind als meine Projekte in einer dynamischeren Sprache, wenn ich mit einer großen Java-App fertig bin.
Es ist jedoch eine schwierige Frage. Ich habe gesehen, wie Leute den "all statischen" Fehler bekommen haben, und es ist ziemlich schwierig, sie davon abzubringen. Aber ich habe gesehen, dass sie ein großes Gefühl der Erleichterung haben, wenn sie darüber hinwegkommen.
quelle
Du hast gefragt:
Vor dieser Frage aufgeführt Sie Polymorphismus und herumgereicht werden als zwei Vorteile der Verwendung von Objekten. Ich möchte sagen, dass dies die Merkmale des OO-Paradigmas sind. Die Kapselung ist ein weiteres Merkmal des OO-Paradigmas.
Dies sind jedoch nicht die Vorteile. Die Vorteile sind:
Du sagtest:
Ich denke, Sie haben dort einen gültigen Punkt. Programmierung ist im Kern nichts anderes als die Transformation von Daten und die Erzeugung von Nebenwirkungen basierend auf Daten. Manchmal erfordert das Transformieren von Daten Hilfsdaten. In anderen Fällen ist dies nicht der Fall.
Wenn Sie sich mit der ersten Kategorie von Transformationen befassen, müssen die Hilfsdaten entweder als Eingabe übergeben oder irgendwo gespeichert werden. Ein Objekt ist der bessere Ansatz als statische Klassen für solche Transformationen. Ein Objekt kann die Hilfsdaten speichern und zum richtigen Zeitpunkt verwenden.
Für die zweite Kategorie von Transformationen ist eine statische Klasse so gut wie ein Objekt, wenn nicht sogar besser. Mathematische Funktionen sind klassische Beispiele für diese Kategorie. Die meisten Standardfunktionen der C-Bibliothek fallen ebenfalls in diese Kategorie.
Du hast gefragt:
Wenn
FileLoader
nicht, überhaupt , müssen alle Daten speichern, ich mit dem zweiten Ansatz gehen würde. Wenn die Wahrscheinlichkeit gering ist, dass für die Durchführung der Operation Zusatzdaten benötigt werden, ist der erste Ansatz sicherer.Du hast gefragt:
Ich habe die Vorteile (Vorteile) der Verwendung des OO-Paradigmas aufgelistet. Ich hoffe, sie sind selbsterklärend. Wenn nicht, werde ich gerne näher darauf eingehen.
Du hast gefragt:
Dies ist ein Beispiel, in dem eine statische Klasse einfach nicht ausreicht. Es gibt viele Möglichkeiten, Ihre Anwendungsdaten in einer Datei zu speichern:
Die einzige Möglichkeit, solche Optionen bereitzustellen, besteht darin, eine Schnittstelle zu erstellen und Objekte zu erstellen, die die Schnittstelle implementieren.
Abschließend
Verwenden Sie den richtigen Ansatz für das jeweilige Problem. Es ist besser, nicht religiös über einen Ansatz gegen den anderen zu sein.
quelle
Melody
,Chord
,Improvisations
,SoundSample
und andere), dann wird es wirtschaftlich sein , die Umsetzung über Abstraktionen zu vereinfachen.Manchmal hängt es von der Sprache und dem Kontext ab. Beispielsweise haben
PHP
Skripte, die zum Bearbeiten von Anforderungen verwendet werden, immer eine Anforderung zum Bearbeiten und eine Antwort zum Generieren während der gesamten Lebensdauer des Skripts. Daher können statische Methoden zum Bearbeiten der Anforderung und zum Generieren einer Antwort geeignet sein. Auf einem Server, auf den geschrieben wurdeNode.js
, können jedoch viele verschiedene Anforderungen und Antworten gleichzeitig vorliegen. Die erste Frage lautet also: Sind Sie sicher, dass die statische Klasse wirklich einem Singleton-Objekt entspricht?Zweitens können Sie mit Singletons mithilfe von Objekten den Polymorphismus mithilfe von Techniken wie Dependency_injection und Factory_method_pattern nutzen . Dies wird häufig in verschiedenen Inversion_of_control- Mustern verwendet und ist nützlich, um Scheinobjekte zum Testen, Protokollieren usw. zu erstellen.
Sie haben die oben genannten Vorteile erwähnt. Hier ist eine, die Sie nicht erwähnt haben: Vererbung. Viele Sprachen können statische Methoden nicht so überschreiben, wie Instanzmethoden überschrieben werden können. Im Allgemeinen ist es viel schwieriger, statische Methoden zu erben und zu überschreiben.
quelle
Zusätzlich zu Rob Ys Post
Solange die Funktionalität von Ihnen
load(File file)
klar von allen anderen von Ihnen verwendeten Funktionen getrennt werden kann, ist es in Ordnung, eine statische Methode / Klasse zu verwenden. Sie externalisieren den Status (was keine schlechte Sache ist) und erhalten auch keine Redundanz, da Sie beispielsweise Ihre Funktion teilweise anwenden oder curry können, damit Sie sich nicht wiederholen müssen. (das ist eigentlich das gleiche oder ähnlich wie bei der Verwendung einesfactory
Musters)Sobald jedoch zwei dieser Funktionen allgemein verwendet werden, möchten Sie sie irgendwie gruppieren können. Stellen Sie sich vor, Sie haben nicht nur eine
load
Funktion, sondern auch einehasValidSyntax
Funktion. Was wirst du tun?Sehen Sie die beiden Verweise
myfile
hier? Sie beginnen sich zu wiederholen, weil Ihr externer Status für jeden Anruf übergeben werden muss. Rob Y hat beschrieben, wie Sie den Zustand (hier denfile
) verinnerlichen , damit Sie es so machen können:quelle
hasValidSyntax()
undload()
sind nicht auf die Wiederverwendung Zustand (das Ergebnis einer teilweise analysierten Datei) erlaubt.Ein wichtiges Thema , wenn statische Klassen ist , dass sie zwingen Sie Ihre Abhängigkeiten zu verbergen, und zwingen Sie auf Implementierungen abhängen. Welche Abhängigkeiten bestehen in der folgenden Konstruktorsignatur:
Nach der Unterschrift zu urteilen, braucht eine Person nur einen Namen, oder? Nun, nicht wenn die Implementierung ist:
Eine Person wird also nicht nur instanziiert. Wir ziehen tatsächlich Daten aus einer Datenbank, die uns einen Pfad zu einer Datei gibt, die dann analysiert werden muss, und dann müssen die tatsächlichen Daten, die wir wollen, herausgeputzt werden. Dieses Beispiel ist deutlich übertrieben, aber es beweist den Punkt. Aber warte, es kann noch so viel mehr geben! Ich schreibe folgenden Test:
Das sollte aber passieren, oder? Ich meine, wir haben all das andere Zeug gekapselt, oder? Nun, eigentlich explodiert es mit einer Ausnahme. Warum? Oh ja, die versteckten Abhängigkeiten, von denen wir nichts wussten, haben einen globalen Zustand. Die
DBConnection
Bedürfnisse werden initialisiert und verbunden. DieFileLoader
Anforderungen werden mit einemFileFormat
Objekt (wieXMLFileFormat
oderCSVFileFormat
) initialisiert . Noch nie davon gehört? Nun, das ist der Punkt. Ihr Code (und Ihr Compiler) können Ihnen nicht sagen, dass Sie diese Dinge benötigen, da die statischen Aufrufe diese Abhängigkeiten verbergen. Habe ich Test gesagt? Ich meinte, dass der neue Junior-Entwickler in Ihrer letzten Version so etwas ausgeliefert hat. Immerhin funktioniert kompiliert =, oder?Angenommen, Sie befinden sich auf einem System, auf dem keine MySQL-Instanz ausgeführt wird. Angenommen, Sie befinden sich auf einem Windows-System, aber Ihre
DBConnection
Klasse wird nur auf einer Linux-Box (mit Linux-Pfaden) an Ihren MySQL-Server gesendet. Oder sagen Sie, Sie befinden sich auf einem System, auf dem der vom zurückgegebene PfadDBConnection
nicht für Sie gelesen / geschrieben wird. Das bedeutet, dass der Versuch, dieses System unter einer dieser Bedingungen auszuführen oder zu testen, fehlschlägt, nicht aufgrund eines Codefehlers, sondern aufgrund eines Entwurfsfehlers, der die Flexibilität des Codes einschränkt und Sie an eine Implementierung bindet.Angenommen, wir möchten jeden Aufruf der Datenbank für eine bestimmte
Person
Instanz protokollieren, die einen bestimmten, problematischen Pfad durchläuft. Wir könntenDBConnection
uns anmelden, aber dies wird alles protokollieren , viel Unordnung verursachen und es schwierig machen, den bestimmten Codepfad zu unterscheiden, den wir verfolgen möchten. Wenn wir jedoch die Abhängigkeitsinjektion mit einerDBConnection
Instanz verwenden, können wir die Schnittstelle einfach in einem Dekorator implementieren (oder die Klasse erweitern, da beide Optionen für ein Objekt verfügbar sind). Mit einer statischen Klasse können wir die Abhängigkeit nicht einfügen, wir können keine Schnittstelle implementieren, wir können sie nicht in einen Dekorator einschließen und wir können die Klasse nicht erweitern. Wir können es nur direkt aufrufen, irgendwo tief in unserem Code versteckt. So sind wir gezwungen eine versteckte Abhängigkeit von einer Implementierung haben.Ist das immer schlecht Nicht unbedingt, aber es könnte besser sein, Ihren Standpunkt umzukehren und zu sagen: "Gibt es einen guten Grund, warum dies keine Instanz sein sollte?" anstatt : „Gibt es einen guten Grund , dies sollte eine Instanz sein?“ Wenn Sie wirklich sagen können, dass Ihr Code
Math.abs()
sowohl in seiner Implementierung als auch in der Art und Weise, wie er verwendet wird, so unerschütterlich (und zustandslos) ist , können Sie in Betracht ziehen, ihn statisch zu machen. Wenn Sie jedoch Instanzen über statische Klassen haben, erhalten Sie eine Welt der Flexibilität, die nachträglich nicht immer wieder leicht zu erfassen ist. Es kann Ihnen auch mehr Klarheit über die wahre Natur der Abhängigkeiten Ihres Codes geben.quelle
new
eine Reihe von Objekten im Konstruktor aufbauen (was im Allgemeinen nicht ratsam ist), aber ich kann sie auch injizieren. Bei statischen Klassen gibt es keine Möglichkeit, sie einzufügen (in Java sowieso nicht einfach, und das wäre eine ganz andere Diskussion für Sprachen, die dies zulassen), daher gibt es keine Wahl. Deshalb habe ich die Idee hervorzuheben ist gezwungen , in meiner Antwort so stark in das Verstecken Abhängigkeiten.Class
, dann stellen wir sicher, dass es die richtige Klasse ist) oder Sie tippen Enten (wir stellen sicher, dass es auf die richtige Methode reagiert). Wenn Sie Letzteres tun möchten, sollten Sie Ihre Sprachwahl neu bewerten. Wenn Sie das erstere tun möchten, funktionieren InstanzenNur meine zwei Cent.
Für Ihre Frage:
Sie können sich fragen, ob sich der Mechanismus des Teils des Systems, der abgespielt wird, oder der Teil des Systems, der Daten in einer Datei speichert, ändert . Wenn Ihre Antwort Ja lautet, bedeutet dies, dass Sie sie mit
abstract class
/ abstrahieren sollteninterface
. Sie fragen sich vielleicht, wie kann ich die zukünftigen Dinge wissen? Auf keinen Fall können wir. Wenn dasjava.lang.Math
Objekt also zustandslos ist, können Sie eine 'statische Klasse' verwenden, z. B. einen objektorientierten Ansatz.quelle