Ich habe mich über gutes objektorientiertes Klassendesign gewundert. Insbesondere fällt es mir schwer, mich zwischen diesen Optionen zu entscheiden:
- statische vs Instanzmethode
- Methode ohne Parameter oder Rückgabewert vs. Methode mit Parametern und Rückgabewert
- überlappende vs unterschiedliche Methode Funktionalität
- private vs public Methode
Beispiel 1:
Diese Implementierung verwendet Instanzmethoden ohne Rückgabewert oder Parameter, ohne überlappende Funktionalität und alle Methoden öffentlich
XmlReader reader = new XmlReader(url);
reader.openUrl();
reader.readXml();
Document result = reader.getDocument();
Beispiel 2:
Diese Implementierung verwendet statische Methoden mit Rückgabewerten und Parametern, mit überlappender Funktionalität und privaten Methoden
Document result = XmlReader.readXml(url);
In Beispiel eins sind alle Methoden öffentliche Instanzen, was den Komponententest vereinfacht. Obwohl alle Methoden unterschiedlich sind, ist readXml () von openUrl () abhängig, da openUrl () zuerst aufgerufen werden muss. Alle Daten werden in Instanzfeldern deklariert, sodass in keiner Methode außer im Konstruktor und in den Accessoren Rückgabewerte oder Parameter vorhanden sind.
In Beispiel zwei ist nur eine Methode öffentlich, der Rest ist statisch, was den Komponententest erschwert. Die Methoden überlappen sich, wenn readXml () openUrl () aufruft. Es gibt keine Felder, alle Daten werden als Parameter in Methoden übergeben und das Ergebnis wird sofort zurückgegeben.
Welchen Grundsätzen sollte ich folgen, um eine ordnungsgemäße objektorientierte Programmierung durchzuführen?
quelle
Antworten:
Beispiel 2 ist ziemlich schlecht zum Testen ... und ich meine nicht, dass Sie die Interna nicht testen können. Sie können Ihr
XmlReader
Objekt auch nicht durch ein Scheinobjekt ersetzen, da Sie überhaupt kein Objekt haben.Beispiel 1 ist unnötig schwer zu verwenden. Wie wäre es mit
Das ist nicht schwieriger zu bedienen als Ihre statische Methode.
Dinge wie das Öffnen der URL, das Lesen von XML, das Konvertieren von Bytes in Zeichenfolgen, das Parsen, das Schließen von Sockets usw. sind uninteressant. Das Erstellen und Verwenden eines Objekts ist wichtig.
Also meiner Meinung nach ist es das richtige OO-Design, nur die beiden Dinge öffentlich zu machen (es sei denn, Sie benötigen aus irgendeinem Grund wirklich die Zwischenschritte). Statik ist böse.
quelle
Document result = new XmlReader(url).getDocument();
Warum? Damit ich es upgraden kannDocument result = whateverReader.getDocument();
undwhateverReader
mir von etwas anderem übergeben habe.Hier ist die Sache - es gibt keine richtige Antwort und es gibt keine absolute Definition für "richtiges objektorientiertes Design" (einige Leute werden dir eine anbieten, aber sie sind naiv ... gib ihnen Zeit).
Es kommt alles auf Ihre Ziele an.
Sie sind Künstler und das Papier ist leer. Sie können ein zartes, fein gezeichnetes Schwarz-Weiß-Seitenporträt oder ein abstraktes Gemälde mit riesigen Streifen gemischter Neons zeichnen. Oder irgendetwas dazwischen.
Also, was fühlt sich für das Problem, das Sie lösen, richtig an? Was sind die Beschwerden der Leute, die Ihre Klassen benutzen müssen, um mit XML zu arbeiten? Was ist schwer an ihrem Job? Welche Art von Code versuchen sie zu schreiben, der die Aufrufe Ihrer Bibliothek umgibt , und wie können Sie dazu beitragen, dass diese besser für sie fließen?
Möchten sie mehr Prägnanz? Möchten sie, dass es sehr klug ist, Standardwerte für Parameter zu ermitteln, damit sie nicht viel (oder nichts) angeben müssen und es richtig errät? Können Sie die für Ihre Bibliothek erforderlichen Setup- und Bereinigungsaufgaben automatisieren, damit sie diese Schritte nicht vergessen können? Was können Sie noch für sie tun?
Was Sie wahrscheinlich tun müssen, ist, es auf vier oder fünf verschiedene Arten zu codieren, dann Ihren Consumer-Hut aufzusetzen und Code zu schreiben, der alle fünf verwendet, und zu sehen, was sich besser anfühlt. Wenn Sie das nicht für Ihre gesamte Bibliothek tun können, dann tun Sie es für eine Teilmenge. Und Sie müssen Ihrer Liste auch einige zusätzliche Alternativen hinzufügen - wie wäre es mit einer fließenden Benutzeroberfläche oder einem funktionaleren Ansatz oder benannten Parametern oder etwas, das auf DynamicObject basiert, damit Sie aussagekräftige "Pseudomethoden" erstellen können, die ihnen helfen aus?
Warum ist jQuery gerade jetzt der König? Da Resig und das Team diesen Prozess befolgten, stießen sie auf ein syntaktisches Prinzip, das die Menge an JS-Code, die für die Arbeit mit dom und Ereignissen erforderlich ist, unglaublich reduzierte. Dieses syntaktische Prinzip war ihnen oder irgendjemand anderem nicht klar, als sie begannen. Sie haben es gefunden.
Als Programmierer ist das Ihre höchste Berufung. Sie tappen im Dunkeln herum, bis Sie es finden. Wenn du das tust, wirst du es wissen. Und Sie geben Ihren Benutzern einen enormen Produktivitätssprung. Und genau darum geht es beim Design (im Softwarebereich).
quelle
Die zweite Option ist besser, da es für die Benutzer einfacher ist (auch wenn es nur Sie sind), viel einfacher.
Zum Testen von Einheiten würde ich nur die Schnittstelle testen, nicht die Interna, wenn Sie die Interna wirklich von privat zu geschützt verschieben möchten.
quelle
Konzentrieren Sie sich auf die Perspektive des Kunden.
Statische Methoden neigen dazu, zukünftige Entwicklungen einzuschränken. Unser Kunde arbeitet mit einer Schnittstelle, die möglicherweise von vielen verschiedenen Implementierungen bereitgestellt wird.
Das Hauptproblem bei Ihrer Open / Read-Sprache ist, dass der Client die Reihenfolge kennen muss, in der Methoden aufgerufen werden, wenn er nur einfache Aufgaben erledigen möchte. Offensichtlich hier, aber in einer größeren Klasse ist es alles andere als offensichtlich.
Die zu testende Hauptmethode ist read (). Interne Methoden können für Testprogramme sichtbar gemacht werden, indem sie weder öffentlich noch privat gemacht werden und Tests in dasselbe Paket gestellt werden. Die Tests können weiterhin vom freigegebenen Code getrennt werden.
quelle
In der Praxis werden Sie feststellen, dass statische Methoden im Allgemeinen auf Dienstprogrammklassen beschränkt sind und Ihre Domänenobjekte, Manager, Controller oder DAOs nicht überladen sollten. Statische Methoden sind am nützlichsten, wenn alle erforderlichen Referenzen in angemessener Weise als Parameter übergeben werden können und einige Funktionen enthalten, die in vielen Klassen wiederverwendet werden können. Wenn Sie feststellen, dass Sie eine statische Methode als Problemumgehung für einen Verweis auf ein Instanzobjekt verwenden, fragen Sie sich, warum Sie nicht stattdessen nur diesen Verweis haben.
Wenn Sie keine Parameter für die Methode benötigen, fügen Sie diese nicht hinzu. Gleiches gilt für den Rückgabewert. Wenn Sie dies berücksichtigen, vereinfachen Sie Ihren Code und stellen sicher, dass Sie nicht für eine Vielzahl von Szenarien programmieren, die niemals eintreten.
Es ist eine gute Idee, Überschneidungen zu vermeiden. Manchmal kann es schwierig sein, aber wenn eine Änderung der Logik erforderlich ist, ist es viel einfacher, eine wiederverwendete Methode zu ändern, als eine ganze Reihe von Methoden mit ähnlichen Funktionen zu ändern
Üblicherweise sollten Getter, Setter und Konstrukteure öffentlich sein. Alles andere möchten Sie versuchen, privat zu bleiben, es sei denn, es gibt einen Fall, in dem eine andere Klasse es ausführen muss. Durch Beibehalten der Standardeinstellung "privat" können Sie die Kapselung beibehalten . Gleiches gilt für Felder, gewöhne dich standardmäßig an private
quelle
1. Statisch vs. Instanz
Ich denke, es gibt sehr klare Richtlinien darüber, was gutes OO-Design ist und was nicht. Das Problem ist, dass die Blogosphäre es schwierig macht, die Guten von den Schlechten und Hässlichen zu trennen. Sie können finden eine Art Referenz sogar die schlechteste Praxis unterstützen Sie denken können.
Und die schlimmste Praxis, die ich mir vorstellen kann, ist Global State, einschließlich der von Ihnen erwähnten Statik und des beliebtesten Singleton aller. Einige Auszüge aus dem klassischen Artikel von Misko Hevery zu diesem Thema .
Daraus ergibt sich, dass Sie keine statischen Verweise auf Objekte bereitstellen sollten, die einen gespeicherten Status haben. Der einzige Ort, an dem ich Statik verwende, ist für aufgezählte Konstanten, und ich habe sogar darüber Bedenken.
2. Methoden mit Eingabeparametern und Rückgabewerten im Vergleich zu Methoden ohne
Sie müssen sich darüber im Klaren sein, dass Methoden ohne Eingabe- und Ausgabeparameter so gut wie garantiert in einem intern gespeicherten Zustand arbeiten (was tun sie sonst?). Es gibt ganze Sprachen , die auf der Idee aufbauen, einen gespeicherten Zustand zu vermeiden.
Jedes Mal, wenn Sie den Status gespeichert haben, haben Sie die Möglichkeit von Nebenwirkungen, stellen Sie also sicher, dass Sie ihn immer mit Bedacht anwenden. Dies bedeutet, dass Sie Funktionen mit definierten Ein- und / oder Ausgängen bevorzugen sollten .
Tatsächlich sind Funktionen mit definierten Ein- und Ausgängen viel einfacher zu testen. Sie müssen hier keine Funktion ausführen und dort nachsehen, um zu sehen, was passiert ist, und Sie müssen keine Eigenschaft irgendwo festlegen bevor Sie die zu testende Funktion ausführen.
Sie können diese Art von Funktion auch sicher als Statik verwenden. Das würde ich aber nicht tun, denn wenn ich später irgendwo eine etwas andere Implementierung dieser Funktion verwenden wollte, anstatt mit der neuen Implementierung eine andere Instanz bereitzustellen, kann ich die Funktionalität nicht ersetzen.
3. Überlappung vs.
Ich verstehe die Frage nicht. Was wäre der Vorteil bei 2 überlappenden Methoden?
4. Privat gegen öffentlich
Belichten Sie nichts, was Sie nicht belichten müssen. Allerdings bin ich auch kein großer Fan von Privat. Ich bin kein C # -Entwickler, sondern ein ActionScript-Entwickler. Ich habe viel Zeit mit dem Flex Framework-Code von Adobe verbracht, der um 2007 geschrieben wurde. Und sie haben einige wirklich schlechte Entscheidungen getroffen, was das Privatisieren zum Albtraum macht, wenn sie versuchen, ihre Klassen zu erweitern.
Wenn Sie also nicht glauben, dass Sie ein besserer Architekt sind als die Adobe-Entwickler um 2007 (Ihrer Frage nach haben Sie wahrscheinlich noch ein paar Jahre Zeit, um diesen Anspruch geltend zu machen), möchten Sie wahrscheinlich nur standardmäßig geschützt werden .
Es gibt einige Probleme mit Ihren Codebeispielen, die bedeuten, dass sie nicht gut strukturiert sind. Daher ist es nicht möglich, A oder B auszuwählen.
Zum einen sollten Sie Ihre Objekterstellung wahrscheinlich von ihrer Verwendung trennen . Normalerweise haben Sie also kein
new XMLReader()
Recht in der Nähe des Verwendungsorts.Wie @djna sagt, sollten Sie auch die in Ihrem XML Reader verwendeten Methoden einkapseln, damit Ihre API (Instanzbeispiel) vereinfacht werden kann:
Ich weiß nicht, wie C # funktioniert, aber da ich mit einer Reihe von Webtechnologien gearbeitet habe, würde ich den Verdacht haben, dass Sie ein XML-Dokument nicht immer sofort zurückgeben können (außer vielleicht als Versprechen oder zukünftiger Typ) Objekt), aber ich kann Ihnen keinen Rat geben, wie mit einer asynchronen Last in C # umgegangen werden soll.
Beachten Sie, dass Sie mit diesem Ansatz mehrere Implementierungen erstellen können, die einen Parameter verwenden, der angibt, wo / was gelesen und ein XML-Objekt zurückgegeben werden soll, und diese basierend auf Ihren Projektanforderungen austauschen können. Beispielsweise können Sie direkt aus einer Datenbank, einem lokalen Geschäft oder, wie in Ihrem ursprünglichen Beispiel, aus einer URL lesen. Sie können das nicht tun, wenn Sie eine statische Methode verwenden.
quelle
Ich werde Ihre Frage nicht beantworten, aber ich denke, dass ein Problem durch die von Ihnen verwendeten Begriffe entsteht. Z.B
Ich denke, Sie brauchen eine XML, also erstelle ich eine Objekt-XML, die aus einem Texttyp erstellt werden kann (ich weiß nicht, C # ... in Java heißt es String). Z.B
Sie können es testen und es ist klar. Wenn Sie dann eine Factory benötigen, können Sie eine Factory-Methode hinzufügen (und diese kann wie Ihr zweites Beispiel statisch sein). Z.B
quelle
Sie können einige einfache Regeln befolgen. Es hilft, wenn Sie die Gründe für die Regeln verstehen.
statische vs instanz methode
Bei Methoden müssen Sie diese Entscheidung nicht bewusst treffen. Wenn es so aussieht, als ob Ihre Methode keine Feldmitglieder verwendet (Ihr bevorzugter Analysator sollte es Ihnen mitteilen), sollten Sie das statische Schlüsselwort hinzufügen.
Methode ohne Parameter oder Rückgabewert vs. Methode mit Parametern und Rückgabewert
Die zweite Option ist aufgrund des Umfangs besser. Sie sollten Ihr Zielfernrohr immer eng halten. Wenn Sie nach Dingen suchen, die Sie brauchen, sollten Sie Eingaben haben, lokal daran arbeiten und ein Ergebnis zurückgeben. Ihre erste Option ist aus dem gleichen Grund schlecht. Globale Variablen sind in der Regel schlecht: Sie sind nur für einen Teil Ihres Codes von Bedeutung, aber sie sind überall sichtbar (und damit Rauschen) und können von überall manipuliert werden. Dies macht es schwierig, ein vollständiges Bild Ihrer Logik zu erhalten.
Überlappende oder unterschiedliche Methodenfunktionen
Ich denke nicht, dass dies ein Problem ist. Methoden, die andere Methoden aufrufen, sind in Ordnung, wenn dadurch Aufgaben in kleinere, deutlich funktionierende Teile zerlegt werden.
private vs public Methode
Machen Sie alles privat, es sei denn, Sie müssen es öffentlich machen. Der Benutzer Ihrer Klasse kann auf den Lärm verzichten, er will nur das sehen, was ihm wichtig ist.
quelle