Actualización (resumen)
Desde que escribí una respuesta bastante detallada, esto es lo que se reduce a:
- Los espacios de nombres son buenos, utilícelos siempre que tenga sentido
- El uso de las clases
inGameIO
y playerIO
probablemente constituiría una violación del SRP. Probablemente significa que está acoplando la forma en que maneja la E / S con la lógica de la aplicación.
- Tenga un par de clases genéricas de E / S, que son usadas (o algunas veces compartidas) por clases de manejador. Estas clases de manejadores luego traducirían la entrada en bruto a un formato que la lógica de la aplicación pueda entender.
- Lo mismo se aplica a la salida: esto se puede realizar mediante clases bastante genéricas, pero pase el estado del juego a través de un objeto de controlador / asignador que traduce el estado interno del juego en algo que las clases de IO genéricas pueden manejar.
Creo que estás viendo esto de manera incorrecta. Está separando el IO en función de los componentes de la aplicación, mientras que, para mí, tiene más sentido tener clases de IO separadas basadas en la fuente, y "type" de IO.
Teniendo algunas clases base / genéricas KeyboardIO
classes MouseIO
para comenzar, y luego, dependiendo de cuándo y dónde las necesite, tenga subclases que manejen dicho IO de manera diferente.
Por ejemplo, la entrada de texto es algo que probablemente quieras manejar de manera diferente a los controles del juego. Descubrirá que desea asignar ciertas claves de manera diferente según cada caso de uso, pero esa asignación no es parte de la IO en sí, es la forma en que maneja la IO.
Siguiendo con el SRP, tendría un par de clases que puedo usar para el IO del teclado. Dependiendo de la situación, probablemente querré interactuar con estas clases de manera diferente, pero su único trabajo es decirme qué está haciendo el usuario.
Luego inyectaría estos objetos en un objeto de controlador que asignaría el IO en bruto a algo con lo que la lógica de mi aplicación podría funcionar (por ejemplo: el usuario presiona "w" , el controlador se asigna a en MOVE_FORWARD
).
Estos controladores, a su vez, se utilizan para hacer que los personajes se muevan y dibujan la pantalla en consecuencia. Una gran simplificación, pero la esencia de esto es este tipo de estructura:
[ IO.Keyboard.InGame ] // generic, if SoC and SRP are strongly adhered to, changing this component should be fairly easy to do
||
==> [ Controls.Keyboard.InGameMapper ]
[ Game.Engine ] <- Controls.Keyboard.InGameMapper
<- IO.Screen
<- ... all sorts of stuff here
InGameMapper.move() //returns MOVE_FORWARD or something
||
==> 1. Game.updateStuff();//do all the things you need to do to move the character in the given direction
2. Game.Screen.SetState(GameState); //translate the game state (inverse handler)
3. IO.Screen.draw();//generate actual output
Lo que tenemos ahora es una clase que es responsable de la E / S del teclado en su forma original. Otra clase que traduce estos datos en algo que el motor del juego puede realmente entender, estos datos luego se usan para actualizar el estado de todos los componentes involucrados, y finalmente, una clase separada se hará cargo de la salida a la pantalla. / p>
Cada clase tiene un solo trabajo: el manejo de la entrada del teclado lo realiza una clase que no sabe / cuida / tiene que saber qué significa la entrada que está procesando. Todo lo que hace es saber cómo para obtener la entrada (almacenada en búfer, no almacenada en búfer, ...).
El manejador traduce esto en una representación interna para que el resto de la aplicación tenga sentido de esta información.
El motor del juego toma los datos que se tradujeron y los usa para notificar a todos los componentes relevantes que algo está sucediendo. Cada uno de estos componentes hace una sola cosa, ya sea que se trate de chequeos de colisión o cambios en la animación de los personajes, no importa, eso depende de cada objeto individual.
Estos objetos retransmiten su estado, y estos datos se pasan a Game.Screen
, que en esencia es un controlador de IO inverso. Asigna la representación interna a algo que el componente IO.Screen
puede usar para generar la salida real.