David West schlug in seinem Buch Object Thinking (Kapitel 10, Abschnitt 1, Unterabschnitt 2) vor, dass in einer idealen OO-Umgebung jedes Objekt in der Lage sein sollte, sich auf Anfrage zu präsentieren. sei es für Menschen (als GUI), nicht native Komponenten (als JSON und / oder XML) oder andere interessierte Parteien:
Objektdenken besagt, dass eine Ansicht (manchmal als Schnittstelle bezeichnet) - grafisch oder auf andere Weise - ein Mittel für ein Objekt ist, um mit einem anderen Objekt zu kommunizieren, und nicht mehr. Die Notwendigkeit einer Ansicht entsteht, wenn sich ein Objekt einem anderen Objekt (normalerweise einem Menschen) oder einer Anwendung (z. B. einer XML-Ansicht für Datenobjekte, die plattformübergreifend gemeinsam genutzt werden) in einer „nicht nativen“ Form präsentieren muss.
Die Ermittlung des Bedarfs und der Parameter, die von einer Ansicht erfüllt werden müssen, zeigt sich in den Szenarien, an denen das Objekt teilnimmt. Wenn ein Objekt aufgefordert wird, sich selbst anzuzeigen, muss es eine Ansicht verwenden - eine Darstellung -, die für den Absender dieser Anzeigemeldung geeignet ist. Wenn beispielsweise ein Objekt versucht, sich selbst zu instanziieren (einen Wert für sich selbst zu erhalten), muss es einem Menschen (oder einem anderen dienstleistenden Objekt) eine Ansicht von sich selbst als implizite Anforderung für einen Wert präsentieren. Wenn wir eine GUI erstellen, die als Vermittler zwischen einem Softwareobjekt und einem menschlichen Objekt dient, verwenden wir Glyphen für Anzeigezwecke und Widgets für Interaktionszwecke.
Aber welche Glyphen und Widgets müssen in die GUI aufgenommen werden? Nur diejenigen, die erforderlich sind, um das Szenario oder die Szenarien 4 von unmittelbarem Interesse abzuschließen, während die Anwendung ausgeführt wird. Diese Perspektive ist für die meisten Entwickler nicht intuitiv, da sie vorschlägt, eine GUI aus der Anwendung heraus zu definieren.
Betrachten Sie als Beispiel eine Brauerei. Auf der einen Seite stehen mit Bier gefüllte Bottiche. An der komplexen Produktionslinie bestehend aus Flaschenwaschanlagen, Füllstationen, Verschließmaschinen und Verpackungsmonteuren. Darüber hinaus gibt es eine Kontrollstation, die die Brauerei überwacht und menschliche Manager über Status und Probleme informiert. Traditionelle Entwickler beginnen wahrscheinlich mit der Analyse und dem Entwurf eines „Brauereimanagementsystems“ aus Sicht des Control Panels. Dies ist analog zum Entwerfen über die Schnittstelle in.
Objektdenken würde stattdessen darauf hindeuten, dass Sie überlegen, welches Objekt der Hauptkunde der Brauerei und all ihrer unzähligen Maschinen ist. In wessen Auftrag existiert das komplexe Labyrinth der Ausrüstung? Die richtige Geschäftsantwort lautet natürlich "Der Kunde". Eine Antwort, die das Denken von Objekten besser widerspiegelt, lautet: „Das Bier.“ Alle Szenarien werden aus der Perspektive des Bieres geschrieben und versuchen, sich in eine Flasche mit einem Verschluss zu packen, der in einem Paket platziert ist und in einem Lastwagen wohnt. Das Bedienfeld ist ein passiver Beobachter 5 des Zustands der Brauerei. Wenn das Bier irgendwann auf ein Problem stößt, liegt es in der Verantwortung des Bieres, die Intervention der menschlichen Bediener anzufordern, indem eine Nachricht an das Bedienfeld (oder an maschinenspezifische Bedienfelder) gesendet wird, in der ein Interventionsdienst angefordert wird.
Diese Perspektive vereinfacht das GUI-Design und beseitigt, was noch wichtiger ist, die Vielzahl von Manager- und Controller-Objekten, die beim Entwerfen aus der Perspektive des Control Panels (GUI) unvermeidlich auftreten.
Von einem Anfänger in der OO-Welt kommend: Sollte das wirklich der Fall sein?
Objekte zu haben, die wissen, wie man sich selbst darstellt, könnte sicherlich die Anzahl der Controller- / Manager-Objekte verringern, die West in seinem Buch wiederholt sagte, ein Objektdenker sollte angeblich versuchen, dies um jeden Preis zu vermeiden. Aber wird die Einhaltung dieser "Regel" nicht gegen SRP verstoßen ?
Auch (wenn sich herausstellt, dass dies der Fall ist), bei einer typischen Implementierung beispielsweise in einer Android-Anwendung: Wie kann man diese Art von Ziel erreichen? Sollte jedes Objekt, das wir erstellen, wissen, wie man sich als präsentiert View
?
quelle
Antworten:
Ich denke, dies ist eines der am schwersten zu verstehenden Dinge über OO-Design und ehrlich gesagt denke ich, dass viele Autoren falsch liegen und / oder es nicht sehr gut erklären. Viele Leute verstehen das falsch und finden es nie wirklich heraus. Nehmen wir ein Beispiel, das nicht auf der GUI basiert, sondern auf dieselbe Gefahr stößt.
In Java hat jedes Objekt eine Methode gleich. Sie haben dann Sammlungstypen wie Set und Map, die von dieser Methode abhängen, um zu bestimmen, wann Objekte zur Sammlung hinzugefügt werden sollen oder wann sie doppelt vorhanden sind. Dies scheint vielen Leuten eine gute OO zu sein. Das Problem ist, dass Sie am Ende ein Objekt (die Sammlung) haben, dessen Verhalten nicht von ihm bestimmt wird, sondern von den darin enthaltenen Objekten. Dies ist ein bisschen so, als würden die Passagiere im Bus direkt dorthin fahren, wo sie hin sollen. Was ist, wenn sie nicht übereinstimmen? Dies ist kein theoretisches Problem, sondern ein wirklich heikles Problem, bei dem Sie die Vererbung grundsätzlich aufheben müssen, um Fehler in Ihrem Programm zu vermeiden. Nimm eine Form und eine farbige Form. Entspricht ein 2x2-Quadrat einem 2x2-blauen Quadrat? Shape sagt "Ja" und ColoredShape sagt "Nein". WHO' ist richtig? Die Antwort hängt davon ab, was in Ihrer Sammlung geschehen soll. Es kann auch nicht davon abhängen, was Sie versuchen zu tun.
Sie werden feststellen, dass dies immer wieder als Problem auftritt. Das Lustige ist, dass es eine Lösung gibt und sie gleich nebenan im Comparable ist. Objekte, die Comparable implementieren, haben dasselbe Rätsel, aber jetzt müssen sie nicht nur feststellen, ob sie gleich sind, sondern auch, ob sie größer als ein anderes Objekt sind. Außerhalb eines sehr engen Anwendungsbereichs ist es wirklich unlösbar. Wir haben also dieses andere Ding namens Komparator. Es ist Aufgabe, zwei Objekte zu betrachten und der Sammlung mitzuteilen, welches größer ist. Alle Probleme, die Sie im vergleichbaren Objekt haben, verschwinden.
Ich kenne dieses Buch nicht und kenne den Autor nicht, aber das Beispiel mit dem Bier scheint überhaupt nicht hilfreich zu sein. Wie würde das Bier wissen, ob es in einer Flasche oder in einem Fass sein sollte und warum sollte es diese Entscheidung treffen? Es ist Aufgabe, gut zu schmecken und Alkohol in den Blutkreislauf der Benutzer zu bringen. Denken wir wirklich, dass Brauereien so funktionieren? "OK Bier, solltest du in einer Flasche oder in einem Fass sein und wenn es eine Flasche ist, sollte es eine 25 Unzen Flasche oder eine 12 Unzen Flasche sein?" Was ist das Bier in diesem Fall (kein Wortspiel beabsichtigt) überhaupt? Ist es ein Tropfen Bier? Vielleicht ist dies nicht im Zusammenhang, aber ich denke, das macht es falsch oder zumindest fügt es diesem Konzept keine Beleuchtung hinzu.
Abgesehen davon gibt es einen Ansatz zum Erstellen von Schnittstellen, den ich verwendet habe, der die Dinge vereinfachen und die OO verbessern kann. Im Wesentlichen erstellen Sie eine Schnittstelle, die die abstrakten Aktionen definiert, die Sie zum Anzeigen des Objekts ausführen können. Möglicherweise haben Sie eine Schnittstelle namens
Display
Methoden wiesetTitle
odersetDescription
wenn Sie das Standard-Java-Namensmuster verwenden. Dann hätte Ihr Objekt eine Methodedisplay(Display display)
(weil dreimal der Reiz ist!) Bei diesem Ansatz muss das Objekt nicht verstehen, was die Schnittstelle ist, es kann Text, Binär, SVG, Bitmap sein, was auch immer, und die Schnittstelle muss nichts über das Objekt wissen . Auf diese Weise kann sich ein Objekt "selbst anzeigen", ohne wissen zu müssen, wie die Anzeige funktioniert. Dieser Ansatz kann die Anzahl der benötigten Wrapper-Klassen erheblich reduzieren, kann jedoch umständlich sein, wenn Sie komplexe Anzeigeanforderungen haben, die je nach Objekt variieren. Sie können es mit Standardansätzen vom Typ MVC mischen, um eine gute Wirkung zu erzielen.quelle
Display
in seiner konkreten Umsetzung funktionieren würde (wie sollten sie waren sie eine Ebene gegeben werdenJFrame
, zum Beispiel ). Vielen Dank.Das Prinzip der Einzelverantwortung bedeutet nicht, dass eine Klasse nur eines tut. Dies bedeutet, dass eine Klasse einen einzigen Grund hat, sich zu ändern.
Sie denken wahrscheinlich an eine Methode , die wirklich nur eines tut.
Aus logischer Sicht würde Ihre Version von SRP bedeuten, dass Sie niemals etwas protokollieren können, da die Protokollierung in einer separaten Verantwortung liegt.
Es ist besser, sich eine Klasse als ein einzelnes, genau definiertes Fach vorzustellen. Sie können verschiedene Methoden verwenden, die dieses Thema unterstützen, und alle können unterschiedliche Aufgaben ausführen.
Die grundlegendste "Display Me" -Methode ist
ToString
, die immer eine Methode für das Objekt ist.Wenn wir eine Benutzeroberfläche erstellen, fördern wir in der Regel die Trennung von Bedenken, indem wir Objekte bereitstellen, deren einziger Zweck darin besteht, Daten von anderen Objekten (z. B. Ansichten) anzuzeigen.
Vielleicht ist ein Beispiel angebracht. Betrachten Sie eine PHP-Website mit einer Ansicht. Eine einfache Ansicht könnte folgendermaßen aussehen:
In PHP enthält
output()
die Ansicht immer eine Funktion. Um die visuelle Darstellung einer Ansicht zu erhalten, müssen Sie lediglich aufrufenoutput()
, und Sie erhalten eine Zeichenfolge, die für die Anzeige in jedem modernen Browser geeignet ist.Wenn Sie bemerken, verweist die Ansicht auf ein Objekt namens
model
. Dies ist das Objekt, das die tatsächlichen Daten enthält. Die Ansicht und das Modell befinden sich in separaten Objekten. Dies ist es, was die Trennung von Bedenken begründet.Die Trennung von Bedenken ist für Websites wichtig, da ein Designer damit neben dem Programmierer an einem Webseiten-Design arbeiten kann .
Weiterführende Literatur
Das MVC-Muster und PHP
quelle
Receipt
Objekt in einer typischen POS-Software, das Sie immer noch aufrufen würden,Receipt.draw(Canvas)
und nichtReceiptView.draw(Receipt)
.Nach den von Ihnen eingefügten Zitaten zu urteilen, interpretieren Sie den Text falsch.
Der Autor sagt nicht, dass jedes Objekt in der Lage sein sollte, sich in einer Benutzeroberfläche zu präsentieren. Dies wäre unmöglich, da ein Objekt nicht wissen kann, in welcher Benutzeroberfläche es angezeigt wird (in einer WinForms-App, einer Linux-App, die XServer als JSON-Zeichenfolge, als XML, als PNG-Image usw. verwendet).
Der Punkt ist, dass Sie spezielle Ansichten schreiben sollten, deren alleinige Verantwortung darin besteht, eine bestimmte Entität anzuzeigen. Sie können beispielsweise eine Ansicht schreiben, die ein Objekt als HTML rendert (wie dies bei Ansichten in MVC-Anwendungen der Fall ist). Sie können einen JSON-Serializer erstellen, der ein Objekt in eine JSON-Zeichenfolge verwandeln kann. Sie können ein Objekt erstellen, das andere Arten von Objekten in PDF-Berichte umwandelt. Es hängt alles ab.
Der Punkt ist, die Geschäftseinheit von der visuellen (oder serialisierten) Darstellung zu trennen. Sie sind verschiedene Dinge und ändern sich aus verschiedenen Gründen.
quelle