Si las clases "ResourceManager" se consideran malas, ¿cuáles son las alternativas?

19

Estoy escuchando opiniones contradictorias como:

  • "Las clases de Gerente dedicado casi nunca son la herramienta de ingeniería adecuada"
  • "Las clases dedicadas de Gerente son (actualmente) la mejor manera de sobrevivir a un gran proyecto con miles de recursos"

Tomemos una clase clásica de ResourceManager que tenga la siguiente funcionalidad:

  • Carga activos (texturas, audio, modelos 3D, etc.)
  • Garantiza que los activos solo se carguen una vez manteniendo un caché
  • La referencia cuenta los activos para determinar si se pueden desasignar
  • Oculta de dónde provienen los activos reales (por ejemplo, podría ser un archivo por activo, o todos los activos en un archivo de paquete, o los activos podrían incluso cargarse a través de la red)
  • Puede recargar recursos sin reiniciar el programa, lo cual es extremadamente útil para los artistas que trabajan en el juego.

Supongamos también el argumento "los singletons son malos" fuera de la mesa, pretendiendo que estos objetos ResourceManager no son singletons, y en su lugar se pasan por inyección de dependencia .

Luego está el argumento "usar una fábrica" ​​o "llamarlo una fábrica". Mi problema con esto es que sí, es una fábrica, pero también es un caché y un cargador (a falta de una palabra mejor). Llamarlo fábrica no lo describe correctamente, y si lo convierto en una fábrica adecuada, ¿dónde se implementan el almacenamiento en caché y la recarga?

Estoy de acuerdo en que las clases "Manager" son a menudo un síntoma de mala arquitectura, pero en este caso particular, ¿cómo podría reestructurarse y aún retener toda la funcionalidad ? ¿Es esta una situación en la que una clase "Manager" es realmente apropiada?

Tom Dalling
fuente
3
¿Tal vez llamarlo un almacén en lugar de una fábrica? : P
caviar desacelerado el

Respuestas:

9

Creo que el problema no es que este sea un mal diseño. El problema es que el nombre "Administrador" es anodino, como "Actualización" y "Proceso" y "Actor". No es útil para alguien que intenta comprender este sistema, y ​​crea confusión si alguna vez tiene otras clases que realizan alguna operación en activos.

Sin embargo, nombrar las cosas de una manera que transmita eficientemente su propósito es muy, muy difícil . La mayoría de los sistemas son demasiado complicados para hacerse obvios por su nombre. Lo mejor que puede hacer es tratar de reducir qué idea específica hace que esta sea una clase coherente, y qué otras clases necesitan ser obviamente diferentes, y elegir una palabra única que se ajuste.

En mi humilde opinión, la característica más importante de un buen nombre es que es único dentro del contexto que la gente lo verá (la base del código), y es fácil de recordar. La prueba debe ser si puede tener una conversación con sus compañeros de equipo que estén familiarizados con el código sin tener que aclarar a qué se refiere. Esto también ayudaría si alguna vez necesita escribir documentación de referencia.

Finalmente, considere que si no puede describir esa idea coherente, puede que no sea muy coherente. Por ejemplo, puede separar el almacenamiento en caché y el recuento de referencias en un AssetCache y mantener la carga en un AssetLoader. Pero eso realmente depende de cuán complicado e entrelazado sea el código.

Pero si su AssetManager es lo suficientemente coherente, y nada más en su juego es AssetManagery, entonces podría ser un nombre perfectamente bueno.

Evan Rogers
fuente
La separación AssetLoader / AssetCache no es una mala idea.
Tom Dalling
4

"¿Hago esto en un lugar o lo hago en muchos?"

La lógica de carga suele estar centralizada en un único punto de acceso porque tiende a ejecutarse solo en los puntos clave de la aplicación, donde estamos tratando de quitarlo lo antes posible. Esto generalmente ocurre en el inicio de la aplicación, en el inicio del nivel de juego o cuando la posición mundial del jugador exige la carga de una nueva porción para mantener la experiencia perfecta. Teniendo en cuenta que esto se hace como un proceso por lotes (ya sea que en un trozo masivo o en muchos más pequeños realmente no importe), tiene sentido tener un método o conjunto de métodos para llamar, para iniciar este proceso.

En la mayoría de las plataformas, la carga es asíncrona, por lo que en realidad tiene pocas opciones, pero mantener separados los flujos de código para la carga y la lógica de otras aplicaciones, otra razón para la abstracción de las funciones de administración de recursos en su propia clase.

Por último, algunos puntos para pensar:

La carga de recursos no se ejecuta con mucha frecuencia en comparación con, por ejemplo, lógica de juego, entonces, ¿vale la pena agregar complejidad a tus objetos de juego individuales para hacer esto? Los objetos del juego generalmente se mantienen lo más livianos posible y están destinados solo a lo que se necesita durante la simulación.

¿Debería un objeto ser responsable de su propia creación y destrucción? Esto puede causar grandes dolores de cabeza durante la gestión de la entidad. Si espera que un objeto realice su propio proceso de creación (incluida la obtención de dependencias de recursos), también tendrá la responsabilidad de destruirse a sí mismo. Esto puede causar punteros colgantes / nulos, por lo que realmente debe gestionarse desde el exterior. Vea esta respuesta que describe ese problema con más detalle.


PD Además de los habituales argumentos de tipo "Los Singleton son terribles" (que son terriblemente dogmáticos), ¿alguna vez has visto un buen argumento contra ResourceManagers? "Las clases de administrador dedicado casi nunca son la herramienta de ingeniería adecuada" no es un argumento para este caso específico .

Ingeniero
fuente