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 1Update()función, y el Modelo contiene una referencia a un objeto Observable, que puede ser una Vista. Cuando el Modelo cambia, llama Update()a todos sus Observables, y la Vista se implementa Update()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:
- Haga que GameEngine se derive de una interfaz Renderable y pase esa referencia al GameState. Si alguna vez refactoriza la parte de representación, solo debe asegurarse de que mantenga la misma interfaz.
- Factoriza la parte de representación en un nuevo objeto Renderer y pasa eso a GameStates en su lugar.
- Déjalo así. Tal vez en algún momento desearás acceder a toda la funcionalidad de GameEngine desde GameState, después de todo. Pero tenga en cuenta que el software que es fácil de mantener y fácil de extender por lo general requiere que cada clase se refiera lo menos posible al exterior, razón por la cual las cosas se dividen en subobjetos e interfaces que realizan un solo y bien- La tarea definida es preferible.