Por ejemplo, suponga que tiene un programa de juego de consola, que tiene todo tipo de métodos de entrada / salida hacia y desde la consola. ¿Sería inteligente para mantener a todos en una sola inputOutput
clase o descomponerlos a más clases específicas como startMenuIO
, inGameIO
, playerIO
, gameBoardIO
, etc., tal que cada clase tiene aproximadamente 1-5 métodos?
Y en la misma nota, si es mejor desglosarlos, ¿sería inteligente colocarlos en un IO
espacio de nombres para que llamarlos sea un poco más detallado, por ejemplo, IO.inGame
etc.?
Respuestas:
Actualización (resumen)
Como he escrito una respuesta bastante detallada, esto es lo que todo se reduce a:
inGameIO
yplayerIO
clases probablemente constituiría una violación del SRP. Probablemente significa que está acoplando la forma en que maneja IO con la lógica de la aplicación.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 el "tipo" de IO.
Tener algunas
KeyboardIO
clases base / genéricasMouseIO
para comenzar, y luego basarse en cuándo y dónde las necesita, tener subclases que manejen dicho IO de manera diferente.Por ejemplo, el ingreso de texto es algo que probablemente quieras manejar de manera diferente a los controles del juego. Tendrás ganas de asignar ciertas teclas de manera diferente dependiendo de cada caso de uso, pero esa asignación no es parte del IO en sí, es cómo manejas el IO.
Siguiendo el SRP, tendría un par de clases que puedo usar para el teclado IO. Dependiendo de la situación, probablemente quiera 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 mapearía el IO sin procesar en algo con lo que la lógica de mi aplicación pueda trabajar (por ejemplo: el usuario presiona "w" , el controlador lo mapea
MOVE_FORWARD
).Estos controladores, a su vez, se utilizan para hacer que los personajes se muevan y dibujar la pantalla en consecuencia. Una simplificación excesiva, pero lo esencial es este tipo de estructura:
Lo que tenemos ahora es una clase que es responsable del teclado IO en su forma cruda. Otra clase que traduce estos datos en algo que el motor del juego realmente puede tener sentido, estos datos se utilizan para actualizar el estado de todos los componentes involucrados y, finalmente, una clase separada se encargará de la salida a la pantalla.
Cada clase tiene un solo trabajo: el manejo de la entrada del teclado se realiza por una clase que no sabe / no importa / tiene que saber qué significa la entrada que está procesando. Todo lo que hace es saber cómo obtener la entrada (con búfer, sin búfer, ...).
El controlador 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 utiliza para notificar a todos los componentes relevantes que algo está sucediendo. Cada uno de estos componentes hace solo una cosa, ya sean controles de colisión o cambios en la animación de personajes, no importa, eso depende de cada objeto individual.
Estos objetos luego retransmiten su estado, y estos datos se pasan a
Game.Screen
, que es esencialmente un manejador de E / S inverso. Mapea la representación interna en algo que elIO.Screen
componente puede usar para generar la salida real.fuente
IO
y losgame
espacios de nombres o clases con subclases?El principio de responsabilidad única puede ser difícil de entender. Lo que he encontrado útil es pensarlo como cómo escribes oraciones. No intentas agrupar muchas ideas en una sola oración. Cada oración debe indicar una idea claramente y diferir los detalles. Por ejemplo, si quisiera definir un automóvil, diría:
Luego definiría cosas como "vehículo", "carretera", "ruedas", etc. por separado. No tratarías de decir:
Del mismo modo, debe intentar que sus clases, métodos, etc., establezcan el concepto central de la manera más simple posible y difieran los detalles a otros métodos y clases. Al igual que al escribir oraciones, no existe una regla estricta sobre cuán grandes deberían ser.
fuente
Yo diría que la mejor manera de hacerlo es mantenerlos en clases separadas. Las clases pequeñas no son malas, de hecho, la mayoría de las veces son una buena idea.
Con respecto a su caso específico, creo que tener el separador puede ayudarlo a cambiar la lógica de cualquiera de esos controladores específicos sin afectar a los demás y, si es necesario, sería más fácil para usted agregar un nuevo método de entrada / salida si fuera necesario.
fuente
El Director de responsabilidad única establece que una clase solo debe tener una razón para cambiar. Si su clase tiene varias razones para cambiar, puede dividirla en otras clases y utilizar la composición para eliminar este problema.
Para responder a su pregunta, tengo que hacerle una pregunta: ¿tiene su clase una sola razón para cambiar? De lo contrario, no tenga miedo de seguir agregando clases más especializadas hasta que cada una tenga una sola razón para cambiar.
fuente