sistem significado ley law hijos hijo griega estricta ejemplos diosa demeter code clean agricultura java android resources accessor law-of-demeter

significado - Java: Acceso a los recursos y la ley de Demeter



ley estricta de demeter (7)

Así que realmente apreciaría si alguien pudiera señalar la mejor manera de proceder y por qué.

La mejor manera, en mi opinión, es mantener la clase de Recursos, hacer que todos los objetos sean privados para no infringir la ley y escribir accesores (pero no para todos los objetos como usted ya descartados).

Tengo muchos objetos y necesito un elemento de acceso para cada método / variable de cada objeto. No es realmente práctico, parece que estoy escribiendo una tonelada de código adicional y, lo que es más importante, hace que la clase de recursos sea ridículamente larga mientras se llena con estos accesores. Por lo tanto, no voy por este camino.

Supongo que muchos objetos son de la misma clase. Por lo tanto, no tiene que hacer un elemento de acceso para cada objeto, lo que realmente explotaría la clase. En un juego, normalmente tienes un héroe, uno o más enemigos y muchos sprites.

public class Resources { private Hero hero; private Enemy enemy; private MenuButtons mainMenuButtons; private Background background; private Scene mainMenu; public void createObjects(){ hero = new Hero(); enemy = new Enemy(); mainMenuButtons = new MenuButtons(); background = new Background(); mainMenu = new Scene(this); } public Hero getBackground() { return background; } public Hero getHero() { return hero; } public List<Enemy> getEnemies() { ArrayList<Enemy> list = new ArrayList<Enemy>(); list.add(enemy); list.add(next_enemy); return list; } public List<Sprite> getSprites() { ArrayList<Sprite> list = new ArrayList<Sprite>(); list.addAll(enemy.getActiveSprites()); return list; } }

En lugar de getHero () y getEnemy (), también puedes crear un método getActor () si Hero y Enemy se derivan de la misma clase. El método getSprites () es solo un ejemplo de cómo podría ser.

Si esa solución no va a funcionar para usted, tengo otra sugerencia.

Haz que la clase de Recursos haga algo de trabajo.

public class ResourceManager { private Hero hero; private Enemy enemy; private MenuButtons mainMenuButtons; private Background background; private Scene mainMenu; public void createObjects(){ hero = new Hero(); enemy = new Enemy(); mainMenuButtons = new MenuButtons(); background = new Background(); mainMenu = new Scene(this); } public void render(Scene scene) { this.background.draw(); if (scene != mainMenu) { this.hero.draw(); this.enemy.draw(); } this.mainMenuButtons.draw(); } public void updateLogic(Scene scene){ this.hero.move(); this.enemy.move(); this.mainMenubuttons.animate(); } }

El mainMenu luego llama a los métodos lógicos directamente en la clase RescourceManager.

public class mainMenu implements Scene { ResourceManager resourceManager; public mainMenu(ResourceManager resourceManager){ this.resourceManager = resourceManager; } @Override public void render(){ resourceManager.render(this); } @Override public void updateLogic(){ resourceManager.updateLogic(this); } }

Espero que mis sugerencias le hayan ayudado un poco a descubrir cómo continuar con su proyecto.

Visión general

En mi juego de Java (Android), tengo una clase llamada recursos . Como su nombre indica, esta clase tiene los recursos para el juego. Todos mis objetos OpenGL (Sprites) se crean aquí

Parece algo parecido a lo siguiente (obviamente, esta es una versión simplificada en comparación con la que aparece en el proyecto real):

public class Resources { Hero hero; Enemy enemy; MenuButtons mainMenuButtons; Background background; Scene mainMenu; public void createObjects(){ hero = new Hero(); enemy = new Enemy(); mainMenuButtons = new MenuButtons(); background = new Background(); mainMenu = new Scene(this); } }

Entonces, dentro de mi escena principal de menú , necesito acceder a mis objetos, para que podamos ver algo como esto:

public class mainMenu implements Scene { Resources resources; public mainMenu(Resources resources){ this.resources = resources; } @Override public void render(){ resources.background.draw(); resources.hero.draw(); resources.enemy.draw(); mainMenuButtons.draw(); } @Override public void updateLogic(){ resources.hero.move(); resources.enemy.move(); resources.mainMenubuttons.animate(); } }

Ahora, el método anterior es solo una forma de obtener acceso a los objetos en recursos y sus métodos. Pero, ¿esto realmente rompe la Ley de Deméter? ¿Si no, porque no? Si es así, ¿cuál es la mejor manera de obtener acceso a estos objetos de una manera que no viole el LOD?

Accesorias?

Una opción (que descarté TBH - ver más abajo) es colocar métodos de acceso en mi clase de recursos . Para que yo pudiera hacer algo como:

resources.drawBackround();

Tengo muchos objetos y necesito un elemento de acceso para cada método / variable de cada objeto. No es realmente práctico, parece que estoy escribiendo una tonelada de código adicional y, lo que es más importante, hace que la clase de recursos sea ridículamente larga mientras se llena con estos accesores. Por lo tanto, no voy por este camino.

Pasando objetos al constructor de la escena.

Por supuesto, también puedo hacer algo como esto:

hero = new Hero(); enemy = new Enemy(); mainMenuButtons = new MenuButtons(); background = new Background(); mainMenu = new Scene(hero, enemy, mainMenuButtons, background);

Así que simplemente puedo hacer esto:

background.draw(); //etc....

Esto es viable para escenas simples (como sistemas de menú que no requieren muchos objetos) pero para el juego principal, podría convertirse rápidamente en un desastre ya que tendría que pasar referencias a más de 30 objetos en el constructor que realmente no suena del todo bien ......

Así que realmente apreciaría si alguien pudiera señalar la mejor manera de proceder y por qué.


Cambia esto:

public void render(){ resources.background.draw(); resources.hero.draw(); resources.enemy.draw(); mainMenuButtons.draw(); } @Override public void updateLogic(){ resources.hero.move(); resources.enemy.move(); resources.mainMenubuttons.animate(); }

Con este:

public void render(){ resources.render(); } @Override public void updateLogic(){ resources.update(); }

ResourceManager no tiene que saber qué hay dentro de los recursos. Puede ser un enemigo o diez, no le importa a ResourceManager.

Y así, en ''Recurso'' puedes hacer:

public void update(){ hero.update();// Cause hero may, or may not move, he makes the choice enemy.update();//... mainMenubuttons.update();//. } public void render(){ ... }

¡Más que esto! puede cambiar la implementación del "Recurso" con una interfaz y estará programando para las interfaces y no para las implementaciones, ¡eso es genial! De esta manera, puede tener un ''Recursos'' para el juego y otro para los menús que se usarán de la misma manera: ¡Solo cambiando, en tiempo de ejecución, los Recursos concretos que estará en un menú o en el juego!

De todos modos, no siempre es necesario rellenar Demeter.


Como se puede ver, no es necesario volver a crear sus recursos, en lugar de eso, utilizan algunos recursos que no se pueden volver a cargar (probablemente imágenes).

Debería compartir el objeto de imágenes dentro de una clase de Recurso, y crear sus objetos dentro de una clase Escena, en el constructor de las entidades puede obtener el recurso compartido que está precargado.


La idea de pasar una lista no es un mal primer paso, pero no es suficiente. Los desarrolladores de juegos tienen un concepto (algo controvertido) de una estructura llamada "gráfico de escena" que les ayuda a realizar un seguimiento de sus recursos (entre otras cosas). https://en.wikipedia.org/?title=Scene_graph

Es un concepto bastante complicado, pero tendrás que aprenderlo más tarde o más temprano. Hay muchos buenos consejos en gamedev.stackexchange.com, así que te sugiero que eches un vistazo allí.

Aquí hay un buen video tutorial de YouTube sobre el tema. https://www.youtube.com/watch?v=ktz9AlMSEoA


Me gusta el concepto de un ResourceManager.
Pero un administrador de recursos debe ser responsable de cargar recursos, almacenarlos y liberarlos.
La representación es definitivamente un método de representación de un objeto.

Así que el Método de representación de Scence podría delegar la representación después de
instanciar un Renderer y alimentarlo con Drawables como el Renderer no lo hace
Recursos de renderización pero objetos rendibles.

Decir:

class MainMenu implements Scene { Renderer sceneRenderer = new Renderer(); AnimatedRenderer animatedRenderer = new AnimatedRenderer(); ResourceManager resourceManager = ResourceManager.getInstance(); List<Resource> resources; List<Drawable> renderedObjects; GameObjectController gameObjectController; void initializeScene() { resources = resourceManager.getResources(); renderedObjects = resourcesAsRenderables(); sceneRenderer.setDrawables(renderedObjects); } List<Drawable> resourcesAsRenderables() { // if resources are not directly renderable, do decoration etc // and return a List of Drawable } @Override public void render(){ sceneRenderer.render(); } @Override public void updateLogic(){ moveGameObjects(); doAnimations(); } protected void moveGameObjects() { gameObjectController.moveAllObjects(this, resources); } protected void doAnimations() { animatedRenderer.render(resources); } class ResourceManager { private static ResourceManager instance = null; List<Resource> resources; public ResourceManager getInstance() { if(instance == null) { instance = new ResourceManager(); instance.loadResources(); } return instance; } private void loadResources() { resources = new LinkedList<Resource>(); resources.add(new Hero()); .... } public List<Resource> getResources() { return resources; } }

Esto claramente separa la lógica y las responsabilidades de las tareas realizadas durante el ciclo de vida de la escena. Un administrador de recursos que es responsable de recuperar recursos y, como pueden demorar mucho tiempo de carga, hace cosas como el almacenamiento en caché o la liberación en situaciones de poca memoria ocultando los detalles al cliente. Un renderizador que es responsable de mostrar los objetos y un controlador que es responsable de mover los objetos. El controlador en sí puede implementar controladores para eventos de teclado, pero eso no es algo que deba ser transparente a la escena. El renderizador puede intercambiar fondos hacia adentro o hacia afuera o escalar o configurar efectos de iluminación, pero la escena solo llama a su método de renderizado. El renderizador animado es responsable de iniciar, renderizar y detener animaciones.


Podría crear una clase de Drawer que maneje el dibujo de todos los objetos. Sus objetos de escena simplemente necesitan alimentar al Drawer los objetos que supongo que son Drawable .

public class Drawer { public void drawObjects(Drawable... objects) { for(Drawable drawable : objects) { drawable.draw(); } } }

Luego, Scene utiliza para dibujar esos objetos.

public class mainMenu implements Scene { Resources resources; Drawer drawer; ... public void render() { drawer.drawObjects(resources.background, resources.hero, resources.enemy, resources.mainMenuButtons); } ... }

Una estrategia similar, utilizando un Updater , puede aplicarse para los otros métodos. Si su método updateLogic() hace que las llamadas sean tan simples como parece, definitivamente puede hacer lo mismo, haciendo que todos esos objetos se hereden de una interfaz Updateable .

public interface Updateable { void update(); }

Hero métodos de update() Hero ''s y Enemy podrían simplemente llamar a sus métodos move() , mientras que update() MenuButtons podría delegar a animate() , etc.

Obviamente, si lo desea, puede usar algún tipo de colección en lugar de varargs para el parámetro de drawObjects() de Drawer . Simplemente me gusta la buena fluidez posible gracias a los varargs, ya que no tienes que crear la colección.

Para obtener otros consejos para mantener el código en línea con la Ley de Demeter, consulte este artículo: Ley de Demeter y cómo trabajar con él


Podría usar la inyección de dependencia y eliminar su clase de Recursos. Luego, puede llamar a funciones en sus campos y no violaría la Ley de Demeter.

Aquí hay un ejemplo de inyección de constructor:

public class MainMenu implements Scene { Background background; Hero hero; Enemy enemy; MenuButtons buttons public mainMenu(Background background, Hero hero, Enemy enemy, MenuButtons buttons){ this.background = background; this.hero = hero; this.enemy = enemy; this.buttons = buttons; } @Override public void render(){ this.background.draw(); this.hero.draw(); this.enemy.draw(); this.mainMenuButtons.draw(); } @Override public void updateLogic(){ this.hero.move(); this.enemy.move(); this.mainMenubuttons.animate(); } }

Con la inyección de dependencia, pasa instancias a constructores y funciones en lugar de "renovarlos" dentro de su clase. Sin embargo, debe administrar sus instancias en algún lugar, y hay muchas bibliotecas que lo harán por usted. Dagger es un popular para Android: square.github.io/dagger