Una asignación en mi clase de ingeniería de software es diseñar una aplicación que pueda jugar diferentes formas en un juego en particular. El juego en cuestión es Mancala, algunos de estos juegos se llaman Wari o Kalah. Estos juegos difieren en algunos aspectos, pero para mi pregunta, solo es importante saber que los juegos podrían diferir en lo siguiente:
- La forma en que se maneja el resultado de un movimiento
- La forma en que se determina el final del juego
- La forma en que se determina el ganador
Lo primero que me vino a la mente al diseñar esto fue utilizar el patrón de estrategia, tengo una variación en los algoritmos (las reglas reales del juego). El diseño podría verse así:
EntoncespenséparamímismoqueeneljuegodeMancalayWari,laformaenquesedeterminaelganadoresexactamentelamismayelcódigoseduplicaría.Nocreoqueestosea,pordefinición,unaviolaciónde'unaregla,unlugar'oelprincipioDRY,yaqueuncambioenlasreglasparaMancalanosignificaríaautomáticamentequeesareglatambiéndeberíacambiarseenWari.Sinembargo,apartirdeloscomentariosquerecibídemiprofesor,tuvelaimpresióndeencontrarundiseñodiferente.
Entoncessemeocurrióesto:
Cada juego (Mancala, Wari, Kalah, ...) tendrá un atributo del tipo de la interfaz de cada regla, es decir, WinnerDeterminer
y si hay una versión de Mancala 2.0 que es la misma que la de Mancala 1.0, excepto por cómo el ganador está determinado a que solo puede usar las versiones de Mancala.
Creo que la implementación de estas reglas como patrón de estrategia es ciertamente válida. Pero el problema real viene cuando quiero diseñarlo más.
Al leer sobre el patrón del método de plantilla, inmediatamente pensé que podría aplicarse a este problema. Las acciones que se realizan cuando un usuario realiza un movimiento son siempre las mismas, y en el mismo orden, a saber:
- depositar piedras en los agujeros (esto es lo mismo para todos los juegos, por lo que se implementaría en el propio método de la plantilla)
- determinar el resultado del movimiento
- determina si el juego ha terminado debido al movimiento anterior
- si el juego ha terminado, determina quién ha ganado
Esos tres últimos pasos están todos en mi patrón de estrategia descrito anteriormente. Estoy teniendo muchos problemas para combinar estos dos. Una posible solución que encontré sería abandonar el patrón de estrategia y hacer lo siguiente:
¿Realmente no veo la diferencia de diseño entre el patrón de estrategia y esto? Pero estoy seguro de que necesito usar un método de plantilla (aunque estaba tan seguro de tener que usar un patrón de estrategia).
Tampoco puedo determinar quién sería responsable de crear el objeto TurnTemplate
, mientras que con el patrón de estrategia siento que tengo familias de objetos (las tres reglas) que podría crear fácilmente usando un patrón abstracto de fábrica. Luego tendría un MancalaRuleFactory
, WariRuleFactory
, etc., crearían las instancias correctas de las reglas y me devolverían un objeto RuleSet
.
Digamos que uso la estrategia + patrón abstracto de fábrica y tengo un objeto RuleSet
que tiene algoritmos para las tres reglas. La única forma en que siento que todavía puedo usar el patrón de método de plantilla con esto es pasar este objeto RuleSet
a mi TurnTemplate
. El 'problema' que luego surge es que nunca necesitaría mis implementaciones concretas del TurnTemplate
, estas clases se volverían obsoletas. En mis métodos protegidos en el TurnTemplate
podría simplemente llamar a ruleSet.determineWinner()
. Como consecuencia, la clase TurnTemplate
ya no sería abstracta sino que tendría que concretarse, ¿sigue siendo un patrón de método de plantilla?
Para resumir, ¿estoy pensando de la manera correcta o me estoy perdiendo algo fácil? Si estoy en el camino correcto, ¿cómo combino un patrón de estrategia y un patrón de método de plantilla?