Angenommen, Sie haben ein Konsolenspielprogramm, das alle Arten von Eingabe- / Ausgabemethoden für und von der Konsole bietet. Wäre es klug sein , sie alle in einem einzigen zu halten inputOutput
Klasse oder sie brechen , um spezifischere Klassen nach unten wie startMenuIO
, inGameIO
, playerIO
, gameBoardIO
etc. , so dass jede Klasse hat etwa 1-5 Methoden?
Und im gleichen Sinne, wenn es besser ist, sie zu zerlegen, wäre es klug, sie in einem IO
Namespace zu platzieren und sie dadurch etwas ausführlicher zu nennen, zB: IO.inGame
etc.?
Antworten:
Update (Rückblick)
Da ich eine ziemlich ausführliche Antwort geschrieben habe, läuft alles auf Folgendes hinaus:
inGameIO
undplayerIO
Klassen würden wahrscheinlich einen Verstoß gegen die SRP darstellen. Dies bedeutet wahrscheinlich, dass Sie die Art und Weise, wie Sie mit E / A umgehen, mit der Anwendungslogik koppeln.Ich denke, Sie sehen das falsch. Sie trennen die E / A in Abhängigkeit von den Komponenten der Anwendung, während es für mich sinnvoller ist, getrennte E / A-Klassen basierend auf der Quelle und dem "Typ" der E / A zu haben .
Wenn Sie einige Basis- / generische
KeyboardIO
Klassen habenMouseIO
, mit denen Sie beginnen sollen, und die dann darauf basieren, wann und wo Sie sie benötigen, verfügen Sie über Unterklassen, die diese E / A unterschiedlich behandeln.Zum Beispiel ist die Texteingabe etwas, mit dem Sie wahrscheinlich anders umgehen möchten als mit spielinternen Steuerelementen. Sie werden feststellen, dass Sie bestimmte Schlüssel je nach Anwendungsfall unterschiedlich zuordnen möchten, aber diese Zuordnung ist nicht Teil der E / A selbst, sondern die Art und Weise, wie Sie mit der E / A umgehen.
Wenn ich mich an die SRP halte, hätte ich ein paar Klassen, die ich für die Tastatur-E / A verwenden kann. Abhängig von der Situation möchte ich wahrscheinlich auf unterschiedliche Weise mit diesen Klassen interagieren, aber ihre einzige Aufgabe ist es, mir mitzuteilen, was der Benutzer tut.
Ich würde diese Objekte dann in ein Handler-Objekt einfügen, das entweder die rohe E / A auf etwas abbildet, mit dem meine Anwendungslogik arbeiten kann (z. B .: Benutzer drückt "w" , der Handler ordnet das zu
MOVE_FORWARD
).Diese Handler werden wiederum verwendet, um die Zeichen in Bewegung zu versetzen und den Bildschirm entsprechend zu zeichnen. Eine grobe Vereinfachung, aber der Kern davon ist diese Art von Struktur:
Was wir jetzt haben, ist eine Klasse, die für die Tastatur-E / A in ihrer Rohform verantwortlich ist. Eine andere Klasse, die diese Daten in etwas übersetzt, woraus die Spiel-Engine tatsächlich Sinn machen kann, verwendet diese Daten dann, um den Status aller beteiligten Komponenten zu aktualisieren, und schließlich kümmert sich eine separate Klasse um die Ausgabe auf dem Bildschirm.
Jede einzelne Klasse hat einen einzelnen Job: Die Bearbeitung der Tastatureingaben erfolgt durch eine Klasse, die nicht weiß / sich darum kümmert / wissen muss, was die Eingabe bedeutet, die sie verarbeitet. Es muss nur wissen, wie die Eingabe abgerufen wird (gepuffert, ungepuffert, ...).
Der Handler übersetzt dies in eine interne Darstellung für den Rest der Anwendung, um diese Informationen zu verstehen.
Die Game-Engine verwendet die übersetzten Daten, um alle relevanten Komponenten über das Geschehen zu informieren. Jede dieser Komponenten macht nur eine Sache, egal ob es sich um Kollisionsprüfungen oder Änderungen der Charakteranimation handelt, es spielt keine Rolle, das hängt von jedem einzelnen Objekt ab.
Diese Objekte geben dann ihren Zustand zurück, und diese Daten werden an
Game.Screen
einen inversen E / A-Handler übergeben. Es ordnet die interne Darstellung einem Element zu, mit dem dieIO.Screen
Komponente die eigentliche Ausgabe generieren kann.quelle
IO
undgame
oder Klassen mit Unterklassen?Das Prinzip der Einzelverantwortung kann schwierig zu verstehen sein. Was ich als nützlich empfunden habe, ist, sich vorzustellen, wie man Sätze schreibt. Sie versuchen nicht, viele Ideen in einen einzigen Satz zu packen. Jeder Satz sollte eine Idee klar wiedergeben und die Details aufschieben. Wenn Sie beispielsweise ein Auto definieren möchten, würden Sie sagen:
Dann würden Sie Dinge wie "Fahrzeug", "Straße", "Räder" usw. separat definieren. Sie würden nicht versuchen zu sagen:
Ebenso sollten Sie versuchen, Ihre Klassen, Methoden usw. so einfach wie möglich zu gestalten, das zentrale Konzept anzugeben und die Details auf andere Methoden und Klassen zu verschieben. Genau wie beim Schreiben von Sätzen gibt es keine feste Regel, wie groß sie sein sollten.
quelle
Ich würde sagen, dass der beste Weg ist, sie in getrennten Klassen zu halten. Kleine Klassen sind nicht schlecht, in der Tat sind sie die meiste Zeit eine gute Idee.
In Bezug auf Ihren speziellen Fall denke ich, dass die Trennung Ihnen dabei helfen kann, die Logik eines dieser speziellen Handler zu ändern, ohne die anderen zu beeinflussen, und es wäre für Sie, falls erforderlich, einfacher, eine neue Eingabe- / Ausgabemethode hinzuzufügen, wenn es dazu käme.
quelle
Das Prinzip der Einzelverantwortung besagt, dass eine Klasse nur einen Grund haben sollte, sich zu ändern. Wenn Ihre Klasse mehrere Änderungsgründe hat, können Sie sie in andere Klassen aufteilen und die Komposition verwenden, um dieses Problem zu beheben.
Um Ihre Frage zu beantworten, muss ich Ihnen eine Frage stellen: Hat Ihre Klasse nur einen Grund, sich zu ändern? Wenn nicht, dann haben Sie keine Angst davor, weiter spezialisierte Klassen hinzuzufügen, bis jeder nur noch einen Grund zum Ändern hat.
quelle