¿Cuál es la mejor manera de resolver una explosión combinatoria de interacciones?

Una de las cosas en las que estoy trabajando ahora tiene algunas similitudes con un juego. Con fines ilustrativos, voy a explicar mi problema utilizando un ejemplo extraído de un juego ficticio e hipotético.

Vamos a llamarloDeathBlaster 4: The Deathening. En DB4, tiene un número deShip objetos que se encuentran periódicamente y al azarPhenomena mientras viajan. Un dadoPhenomenon puede tener cero, uno o másEffects en unShip que lo encuentra. Por ejemplo, podríamos tener cuatro tipos deShips y tres tipos dePhenomena.

                              Phenomena
              ==========================================
Ships         GravityWell     BlackHole      NebulaField
------------  ------------------------------------------
RedShip       +20% speed      -50% power     -50% shield
BlueShip      no effect       invulnerable   death              Effects of Various
GreenShip     -20% speed      death          +50% shield        Phenomena on Ships
YellowShip    death           +50% power     no effect    

Adicionalmente,Effects pueden interactuar entre sí. Por ejemplo, unaGreenShip que está tanto en unGravityWell y unNebulaField puede derivar algún tipo de sinergia entre el @ generaSpeedEffect yShieldEffect. En tales casos, el efecto sinérgico es en sí mismo unEffect - por ejemplo, puede haber unaPowerLevelSynergyEffect que resulta de esta interacción. No hay otra información que no sea el conjunto deEffects actuando en unShip es necesario para resolver cuál debería ser el resultado final.

Puede comenzar a ver un problema emergente aquí. Como un primer acercamiento ingenuo, o cadaShip tendrá que saber cómo manejar cadaPhenomenon o cadaPhenomenon tendrá que saber sobre cadaShip. Obviamente, esto es inaceptable, por lo que nos gustaría trasladar estas responsabilidades a otra parte. Claramente hay al menos una clase externa aquí, tal vez unaMediator oVisitor de algún tipo

Pero, ¿cuál es la mejor manera de hacer eso? La solución ideal probablemente tendrá estas propiedades:

Es tan fácil agregar una nuevaShip como es agregar una nuevaPhenomenon. Las interacciones que no producen ningún efecto son las predeterminadas y no requieren código adicional para representarlas. Convención sobre configuración Comprende cómoEffects interactúa entre sí y es capaz de gestionar estas interacciones para decidir cuál será el resultado final.

Ya he decidido cuál será mi enfoque, creo, pero me interesa saber cuál es el mejor consenso de diseño. ¿Por dónde empezarías? ¿Qué caminos explorarías?

Actualización de seguimiento: Gracias por sus respuestas a todos. Esto es lo que terminé haciendo. Mi observación principal fue que el número de diferentesEffects parece ser pequeño en relación con el número de posiblesPhenomena × Ships interacciones. Es decir, aunque hay muchas posibles combinaciones de interacciones, el número de tipos de resultados de esas interacciones es un número menor.

uede ver que, por ejemplo, aunque hay 12 combinaciones de interacción en la tabla, solo hay cinco tipos de efectos: modificaciones a la velocidad, modificaciones al poder, modificaciones al escudo, invulnerabilidad, muerte.

Introduje una tercera clase, laInteractionResolver, para determinar el resultado de las interacciones. Contiene un diccionario que asignaShip-Phenomenon pares aEffects (que son básicamente un delegado que realiza el efecto y algunos metadatos). CadaShip recibe unaEffectStack correspondiente a laEffects está experimentando cuando se completa el resultado de calcular la interacción.

Ships luego use laEffectStack para determinar el resultado real de laEffects en ellos, agregando modificadores a sus atributos y propiedades existentes.

Me gusta esto porque:

os barcos nunca necesitan saber sobre los fenómenoLa complejidad de la relación Barco-Fenómenos se abstrae en InteractionResolver. Los detalles de cómo resolver efectos múltiples y posiblemente complejos se abstraen conInteractionResolver. Los barcos solo tienen que aplicar los efectos según sea necesario. Esto permite refactorizaciones útiles adicionales. Por ejemplo, lacamin en el que un barco procesa los efectos se pueden diferenciar haciendo unEffectProcessorStrategy. El valor predeterminado podría ser procesar todos los efectos, pero, digamos, unBossShip podría ignorar efectos menores al tener una @ diferenEffectProcessorStrategy.

Respuestas a la pregunta(7)

Su respuesta a la pregunta