Digamos que tengo un creador de exámenes, para que los maestros puedan crear un montón de preguntas para un examen.
Sin embargo, no todas las preguntas son iguales: tiene opción múltiple, cuadro de texto, coincidencia, etc. Cada uno de estos tipos de preguntas necesita almacenar diferentes tipos de datos y una GUI diferente tanto para el creador como para el examinado.
Me gustaría evitar dos cosas:
- Verificaciones de tipo o fundición de tipos
- Cualquier cosa relacionada con la GUI en mi código de datos.
En mi intento inicial, termino con las siguientes clases:
class Test{
List<Question> questions;
}
interface Question { }
class MultipleChoice implements Question {}
class TextBox implements Question {}
Sin embargo, cuando voy a mostrar la prueba, inevitablemente terminaré con un código como:
for (Question question: questions){
if (question instanceof MultipleChoice){
display.add(new MultipleChoiceViewer());
}
//etc
}
Esto se siente como un problema muy común. ¿Hay algún patrón de diseño que me permita tener preguntas polimórficas mientras evito los elementos mencionados anteriormente? ¿O es el polimorfismo la idea equivocada en primer lugar?
fuente
Respuestas:
Puede usar un patrón de visitante:
Otra opción es una unión discriminada. Esto dependerá mucho de tu idioma. Esto es mucho mejor si su idioma lo admite, pero muchos idiomas populares no.
fuente
visit
(el visitante visita). También se suele llamar al método en los objetos que se visitanaccept(Visitor)
(el objeto acepta un visitante). Ver oodesign.com/visitor-pattern.htmlEn C # / WPF (y, me imagino, en otros lenguajes de diseño centrados en la interfaz de usuario), tenemos DataTemplates . Al definir plantillas de datos, crea una asociación entre un tipo de "objeto de datos" y una "plantilla de IU" especializada creada específicamente para mostrar ese objeto.
Una vez que proporcione instrucciones para que la IU cargue un tipo específico de objeto, verá si hay alguna plantilla de datos definida para el objeto.
fuente
Si cada respuesta puede codificarse como una cadena, puede hacer esto:
Donde la cadena vacía significa es una pregunta que aún no tiene respuesta. Esto permite que las preguntas, las respuestas y la GUI se separen, pero permite el polimorfismo.
El cuadro de texto, la coincidencia, etc. podrían tener diseños similares, todos implementando la interfaz de preguntas. La construcción de la cadena de respuesta ocurre en la vista. Las cadenas de respuesta representan el estado de la prueba. Deben almacenarse a medida que el alumno progresa. Aplicarlas a las preguntas permite mostrar la prueba y su estado de manera calificada y no calificada.
Al separar la salida en
display()
ydisplayGraded()
la vista no necesita intercambiarse y no es necesario realizar ramificaciones en los parámetros. Sin embargo, cada vista es libre de reutilizar tanta lógica de visualización como sea posible cuando se visualiza. Cualquiera sea el esquema diseñado para hacer eso, no necesita filtrarse en este código.Sin embargo, si desea tener un control más dinámico de cómo se muestra una pregunta, puede hacer esto:
y esto
Esto tiene el inconveniente de que requiere vistas que no pretenden mostrar
score()
oanswerKey
depender de ellas cuando no las necesitan. Pero significa que no tiene que reconstruir las preguntas de la prueba para cada tipo de vista que desea usar.fuente
En mi opinión, si necesita una característica tan genérica, disminuiría el acoplamiento entre las cosas en el código. Intentaría definir el tipo de Pregunta lo más genérico posible, y luego crearía diferentes clases para los objetos de representación. Por favor, vea los ejemplos a continuación:
Luego, para la parte de representación, eliminé la verificación de Tipo implementando una verificación simple en los datos dentro del objeto de pregunta. El siguiente código intenta lograr dos cosas: (i) evitar la verificación de tipo y evitar la violación del principio "L" (sustitución de Liskov en SÓLIDO) eliminando el subtipo de clase de Pregunta; y (ii) hacer que el código sea extensible, nunca cambiando el código de representación principal a continuación, simplemente agregando más implementaciones de QuestionView y sus instancias a la matriz (este es en realidad el principio "O" en SOLID: abierto para extensión y cerrado para modificación).
fuente
Una fábrica debería poder hacer esto. El mapa reemplaza la declaración de cambio, que se necesita únicamente para emparejar la Pregunta (que no sabe nada sobre la vista) con el QuestionView.
Con esto, la vista utiliza el tipo específico de Pregunta que puede mostrar, y el modelo permanece desconectado de la vista.
La fábrica podría llenarse mediante reflexión o manualmente al inicio de la aplicación.
fuente
Question
un papelMultipleChoiceQuestion
cuando crees elMultipleChoiceView
No estoy seguro de que esto cuente como "evitar verificaciones de tipo", dependiendo de cómo te sientas con respecto a la reflexión .
fuente
if
verificación de tipo a unadictionary
verificación de tipo. Como cómo Python usa diccionarios en lugar de declaraciones de cambio. Dicho esto, me gusta esta manera más de una lista de sentencias if.template <typename Q> struct question_traits;
con las especializaciones apropiadas