¿Es un mal diseño tener 2 clases que se necesitan mutuamente?
Estoy escribiendo un pequeño juego en el que tengo una GameEngine
clase que tiene algunos GameState
objetos. Para acceder a varios métodos de representación, estos GameState
objetos también necesitan conocer la GameEngine
clase, por lo que es una dependencia circular.
¿Llamarías a este mal diseño? Solo pregunto, porque no estoy muy seguro y en este momento todavía puedo refactorizar estas cosas.
c++
architecture
shad0w
fuente
fuente
Respuestas:
No es un mal diseño por naturaleza, pero puede salirse fácilmente de control. De hecho, está utilizando ese diseño en sus códigos cotidianos. Por ejemplo, vector sabe que es el primer iterador y los iteradores tienen un puntero a su contenedor.
Ahora en su caso, es mucho mejor tener dos clases separadas para GameEnigne y GameState. Dado que básicamente esos dos están haciendo cosas diferentes, y más tarde puede definir muchas clases que heredan GameState (como un GameState para cada escena en su juego). Y nadie puede negar su necesidad de tener acceso el uno al otro. Básicamente GameEngine está ejecutando gamestates, por lo que debería tener un puntero hacia ellos. Y GameState está utilizando recursos definidos en GameEngine (como renderizado, administrador de física, etc.).
Tampoco puedes y no debes combinar esas dos clases entre sí, ya que están haciendo cosas diferentes por naturaleza y la combinación dará como resultado una clase muy grande que a nadie le gusta.
Hasta ahora sabemos que necesitamos dependencia circular en nuestro diseño. Hay varias formas de crear eso de forma segura:
Para concluir su respuesta, puede usar cualquiera de esos tres métodos, pero debe tener cuidado de no usar ninguno de ellos, ya que, como dije, todos esos diseños son realmente peligrosos y podrían resultar fácilmente en un código que no se puede mantener.
fuente
Es un poco un olor a código , pero uno puede irse con él. Si esa es la forma más fácil y rápida de poner en marcha su juego, hágalo. Pero tenga esto en cuenta porque hay una buena posibilidad de que tenga que refactorizarlo en algún momento.
La cuestión con C ++ es que las dependencias circulares no se compilarán tan fácilmente , por lo que podría ser una mejor idea deshacerse de ellas en lugar de perder tiempo arreglando su compilación.
Vea esta pregunta en SO para algunas opiniones más.
No, sigue siendo mejor que poner todo en una clase.
No es tan bueno, pero en realidad está bastante cerca de la mayoría de las implementaciones que he visto. Por lo general, tendrías una clase de administrador para estados de juego ( ¡cuidado! ) Y una clase de renderizador, y es bastante común que sean singletons. Entonces, la dependencia circular está "oculta", pero está potencialmente allí.
Además, como se te dijo en los comentarios, es un poco extraño que las clases de estado del juego realicen algún tipo de representación. Solo deben contener información de estado, y el renderizado debe ser manejado por un renderizador o algún componente gráfico de los objetos del juego.
Ahora puede haber el mejor diseño. Tengo curiosidad por ver si otras respuestas traen una buena idea. Aún así, probablemente eres el que puede encontrar el mejor diseño para tu juego.
fuente
A menudo se considera un mal diseño tener que tener 2 clases que se refieren directamente entre sí, sí. En términos prácticos, puede ser más difícil seguir el flujo de control a través del código, la propiedad de los objetos y su vida útil puede ser complicada, significa que ninguna de las clases es reutilizable sin la otra, podría significar que el flujo de control realmente debería vivir fuera de ambos de estas clases en una tercera clase 'mediadora', y así sucesivamente.
Sin embargo, es muy común que 2 objetos se refieran entre sí, y la diferencia aquí es que generalmente la relación en una dirección es más abstracta. Por ejemplo, en un sistema Modelo-Vista-Controlador, el objeto Vista puede contener una referencia al objeto Modelo, y sabrá todo sobre él, pudiendo acceder a todos sus métodos y propiedades para que la Vista pueda rellenarse con datos relevantes . El objeto Modelo puede contener una referencia a la Vista para que pueda actualizar la Vista cuando sus propios datos hayan cambiado. Pero en lugar de que el Modelo tenga una referencia de Vista, lo que haría que el Modelo dependiera de la Vista, generalmente la Vista implementa una interfaz Observable, a menudo con solo 1
Update()
función, y el Modelo contiene una referencia a un objeto Observable, que puede ser una Vista. Cuando el Modelo cambia, llamaUpdate()
a todos sus Observables, y la Vista se implementaUpdate()
llamando nuevamente al Modelo y recuperando cualquier información actualizada. El beneficio aquí es que el Modelo no sabe nada sobre Vistas (¿y por qué debería hacerlo?), Y puede reutilizarse en otras situaciones, incluso aquellas sin Vistas.Tienes una situación similar en tu juego. GameEngine normalmente sabrá sobre GameStates. Pero GameState no necesita saber todo sobre GameEngine, solo necesita acceder a ciertos métodos de representación en GameEngine. Eso debería activar una pequeña alarma en tu cabeza que dice que (a) GameEngine está tratando de hacer demasiadas cosas dentro de una clase, y / o (b) GameState no necesita todo el motor del juego, solo la parte renderizable.
Tres enfoques para resolver esto incluyen:
fuente
En general, se considera una buena práctica tener una alta cohesión con un bajo acoplamiento. Aquí hay algunos enlaces sobre el acoplamiento y la cohesión.
acoplamiento wikipedia
cohesión de wikipedia
bajo acoplamiento, alta cohesión
stackoverflow en mejores prácticas
Tener dos clases de referencia entre sí es tener un alto acoplamiento. El marco de Google Guice tiene como objetivo lograr una alta cohesión con un bajo acoplamiento a través de medios de inyección de dependencia. Te sugiero que leas un poco más sobre el tema y luego hagas tu propia llamada dado tu contexto.
fuente