Nehmen wir an, ich habe einen Testbuilder, damit die Lehrer eine Reihe von Fragen für einen Test erstellen können.
Es sind jedoch nicht alle Fragen gleich: Sie haben mehrere Auswahlmöglichkeiten, Textfelder, Übereinstimmungen usw. Jeder dieser Fragetypen muss unterschiedliche Datentypen speichern und sowohl für den Ersteller als auch für den Testteilnehmer eine unterschiedliche Benutzeroberfläche benötigen.
Ich möchte zwei Dinge vermeiden:
- Typprüfungen oder Typprüfungen
- Alles, was mit der GUI in meinem Datencode zu tun hat.
Bei meinem ersten Versuch lande ich in den folgenden Klassen:
class Test{
List<Question> questions;
}
interface Question { }
class MultipleChoice implements Question {}
class TextBox implements Question {}
Wenn ich jedoch zum Anzeigen des Tests gehe, erhalte ich zwangsläufig folgenden Code:
for (Question question: questions){
if (question instanceof MultipleChoice){
display.add(new MultipleChoiceViewer());
}
//etc
}
Dies scheint ein sehr häufiges Problem zu sein. Gibt es ein Entwurfsmuster, das es mir ermöglicht, polymorphe Fragen zu stellen und dabei die oben aufgeführten Punkte zu vermeiden? Oder ist Polymorphismus überhaupt die falsche Idee?
quelle
Antworten:
Sie können ein Besuchermuster verwenden:
Eine andere Option ist eine diskriminierte Gewerkschaft. Dies hängt sehr stark von Ihrer Sprache ab. Dies ist viel besser, wenn Ihre Sprache dies unterstützt, aber viele gängige Sprachen nicht.
quelle
visit
(der Besucher besucht). Auch die Methode in den besuchten Objekten wird normalerweise aufgerufenaccept(Visitor)
(das Objekt akzeptiert einen Besucher). Siehe oodesign.com/visitor-pattern.htmlIn C # / WPF (und, wie ich mir vorstellen kann, in anderen auf die Benutzeroberfläche ausgerichteten Entwurfssprachen) verfügen wir über DataTemplates . Durch das Definieren von Datenvorlagen erstellen Sie eine Zuordnung zwischen einem Typ von "Datenobjekt" und einer speziellen "UI-Vorlage", die speziell für die Anzeige dieses Objekts erstellt wurde.
Sobald Sie der Benutzeroberfläche Anweisungen zum Laden eines bestimmten Objekttyps gegeben haben, wird angezeigt, ob für das Objekt Datenvorlagen definiert sind.
quelle
Wenn jede Antwort als Zeichenfolge codiert werden kann, können Sie dies tun:
Wo die leere Zeichenfolge eine Frage bedeutet, auf die es noch keine Antwort gibt. Auf diese Weise können die Fragen, die Antworten und die Benutzeroberfläche getrennt werden, ohne dass ein Polymorphismus auftritt.
Textfelder, Übereinstimmungen usw. könnten ähnliche Designs aufweisen und alle die Fragenschnittstelle implementieren. Der Aufbau des Antwortstrings erfolgt in der Ansicht. Die Antwortzeichenfolgen geben den Status des Tests an. Sie sollten im Verlauf des Studiums gespeichert werden. Wenn Sie sie auf die Fragen anwenden, können Sie den Test und seinen Status sowohl benotet als auch unbenotet anzeigen.
Durch das Trennen der Ausgabe in
display()
und mussdisplayGraded()
die Ansicht nicht ausgetauscht werden und es muss keine Verzweigung der Parameter vorgenommen werden. Es steht jedoch jeder Ansicht frei, so viel Anzeigelogik wie möglich beim Anzeigen wiederzuverwenden. Welches Schema auch immer entwickelt wurde, es muss nicht in diesen Code eindringen.Wenn Sie jedoch eine dynamischere Steuerung der Anzeige einer Frage wünschen, können Sie dies tun:
und das
Dies hat den Nachteil, dass Ansichten erforderlich sind, die das nicht anzeigen
score()
oderanswerKey
von ihnen abhängig sind, wenn sie sie nicht benötigen. Dies bedeutet jedoch, dass Sie die Testfragen nicht für jede Art von Ansicht neu erstellen müssen, die Sie verwenden möchten.quelle
Wenn Sie eine solche generische Funktion benötigen, würde ich meiner Meinung nach die Kopplung zwischen den Elementen im Code verringern. Ich würde versuchen, den Fragentyp so allgemein wie möglich zu definieren, und danach würde ich verschiedene Klassen für die Renderer-Objekte erstellen. Bitte beachten Sie die folgenden Beispiele:
Dann entfernte ich für den Rendering-Teil die Typprüfung, indem ich eine einfache Prüfung der Daten im Fragenobjekt implementierte. Der folgende Code versucht, zwei Dinge zu erreichen: (i) Vermeiden Sie die Typprüfung und die Verletzung des "L" -Prinzips (Liskov-Substitution in SOLID), indem Sie die Untertypisierung der Frageklasse entfernen. und (ii) den Code erweiterbar machen, indem der Kern-Rendering-Code unten niemals geändert wird, indem dem Array lediglich weitere QuestionView-Implementierungen und deren Instanzen hinzugefügt werden (dies ist tatsächlich das "O" -Prinzip in SOLID - offen für Erweiterungen und geschlossen für Änderungen).
quelle
Eine Fabrik sollte dazu in der Lage sein. Die Map ersetzt die switch-Anweisung, die nur zum Koppeln der Frage (die nichts über die Ansicht weiß) mit der QuestionView erforderlich ist.
Damit verwendet die Ansicht den spezifischen Fragentyp, den sie anzeigen kann, und das Modell bleibt von der Ansicht getrennt.
Die Factory kann über Reflection oder manuell beim Start der Anwendung aufgefüllt werden.
quelle
Question
in ein ,MultipleChoiceQuestion
wenn Sie das schaffenMultipleChoiceView
Ich bin mir nicht sicher, ob dies als "Vermeiden von Typprüfungen" gilt, je nachdem, wie Sie über Reflexion denken .
quelle
if
Typprüfung zu einerdictionary
Typprüfung. So wie Python Wörterbücher anstelle von switch-Anweisungen verwendet. Das heißt, ich mag auf diese Weise mehr als eine Liste von if-Anweisungen.template <typename Q> struct question_traits;
mit entsprechenden Spezialisierungen empfehlen