Java: Zugriff auf Ressourcen und das Gesetz von Demeter

Überblick

In meinem (Android) Java-Spiel habe ich eine Klasse namens Ressourcen . Wie der Name schon sagt, hält diese Klasse die Ressourcen für das Spiel. Hier werden alle meine OpenGL-Objekte (Sprites) erstellt

Es sieht so aus wie das folgende (offensichtlich ist dies eine vereinfachte Version im Vergleich zu dem, was im realen Projekt erscheint):

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); } } 

Also, in meiner mainMenu- Szene, brauche ich Zugang zu meinen Objekten, also können wir so etwas sehen:

 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(); } } 

Nun ist die obige Methode nur eine Möglichkeit, Zugang zu den Objekten in Ressourcen und deren Methoden zu erhalten. Aber macht das eigentlich das Gesetz von Demeter? Wenn nicht, warum nicht? Wenn ja, was ist der beste Weg, um Zugang zu diesen Objekten in einer Weise, die nicht gegen die LOD?

Zubehör?

Eine Option (die ich ausgeschlossen habe TBH – siehe unten) platziert Accessor-Methoden in meine Ressourcen- Klasse. So dass ich so etwas machen könnte:

 resources.drawBackround(); 

Ich habe viele Objekte und ich brauche einen Accessor für jede Methode / Variable jedes Objekts. Nicht wirklich praktisch, es scheint, als würde ich eine Tonne extra Code schreiben und am wichtigsten, es macht die Ressourcen Klasse lächerlich lang, wie es mit diesen Accessoren gefüllt wird. Deshalb gehe ich nicht auf diesen Weg.

Übergeben von Objekten in den Konstrukteur der Szene

Natürlich kann ich auch so etwas machen:

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

So kann ich das einfach machen:

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

Das ist praktisch für einfache Szenen (wie zB Menüsysteme, die nicht viele Objekte benötigen), aber für das Hauptspiel könnte es schnell ein Chaos werden, da ich Referenzen auf etwa 30+ Objekte in den Konstruktor übergeben musste Klingt nicht richtig richtig ……

Also würde ich wirklich schätzen, wenn jemand auf den besten Weg hinweisen könnte und warum.

7 Solutions collect form web for “Java: Zugriff auf Ressourcen und das Gesetz von Demeter”

Also würde ich wirklich schätzen, wenn jemand auf den besten Weg hinweisen könnte und warum.

Der beste Weg, meiner Meinung nach, ist es, die Resources-Klasse zu behalten, alle Objekte privat zu machen, um das Gesetz nicht zu brechen und Züge zu schreiben (aber nicht für jedes Objekt wie du bereits ausgeschlossen).

Ich habe viele Objekte und ich brauche einen Accessor für jede Methode / Variable jedes Objekts. Nicht wirklich praktisch, es scheint, als würde ich eine Tonne extra Code schreiben und am wichtigsten, es macht die Ressourcen Klasse lächerlich lang, wie es mit diesen Accessoren gefüllt wird. Deshalb gehe ich nicht auf diesen Weg.

Ich nehme an, viele Gegenstände sind von der gleichen Klasse. Also musst du keinen Accessor für jeden Gegenstand machen, was die Klasse wirklich sprengen würde. Ich habe ein Spiel, das du normalerweise einen Helden hast, einen oder mehrere Feinde und viele 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; } } 

Anstelle von getHero () und getEnemy () kannst du auch eine getActor () Methode machen, wenn Hero und Enemy aus derselben Klasse abgeleitet sind. Die Methode getSprites () ist nur ein Beispiel, wie es aussehen könnte.

Wenn diese Lösung nicht für dich arbeiten wird, habe ich einen weiteren Vorschlag.

Machen Sie die Ressourcen-Klasse machen einige Arbeit.

 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(); } } 

Der mainMenu ruft dann logische Methoden direkt in der RescourceManager-Klasse auf.

 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); } } 

Ich hoffe, meine Vorschläge haben Ihnen geholfen, etwas herauszufinden, wie Sie mit Ihrem Projekt fortsetzen können.

Sie können die Abhängigkeitsinjektion verwenden und Ihre Ressourcenklasse beseitigen. Dann können Sie Funktionen auf Ihren Feldern anrufen und würden nicht gegen das Gesetz von Demeter verstoßen.

Hier ist ein Beispiel mit Konstruktorinjektion:

 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(); } } 

Mit Abhängigkeitsinjektion übergeben Sie Instanzen in Konstruktoren und Funktionen, anstatt sie in Ihrer Klasse zu "neu zu machen". Du musst deine Instanzen irgendwo hingehen, aber es gibt viele Bibliotheken, die das für dich tun werden. Dagger ist ein beliebtes für Android: http://square.github.io/dagger/

Die Idee, eine Liste zu übergeben, ist kein schlechter erster Schritt, aber es reicht nicht aus. Game-Entwickler haben ein (etwas umstrittenes) Konzept einer Struktur namens "Szene-Grafik", die ihnen hilft, ihre Ressourcen zu verfolgen (unter anderem). https://de.wikipedia.org/?title=Scene_graph

Es ist ein ziemlich kompliziertes Konzept, aber du wirst es früher oder später lernen müssen. Es gibt eine Menge guter Rat auf gamedev.stackexchange.com, also würde ich vorschlagen, dass Sie einen Blick über dort nehmen.

Hier ist ein schönes YouTube-Video-Tutorial zum Thema. https://www.youtube.com/watch?v=ktz9AlMSEoA

Sie können eine Drawer erstellen, die die Zeichnung aller Objekte behandelt. Ihre Drawable müssen einfach die Drawer füttern, die Objekte, die ich Drawable , sind Drawable .

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

Dies wird dann von Scene um diese Objekte zu zeichnen.

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

Eine ähnliche Strategie, die einen Updater , kann für die anderen Methoden angewendet werden. Wenn deine updateLogic() -Methode so einfach von Anrufen macht, wie sie aussieht, kannst du definitiv dasselbe tun, indem du all diese Objekte von einer Updateable Schnittstelle erben Updateable .

 public interface Updateable { void update(); } 

Hero 's und Enemy ' s update() Methoden könnten einfach ihre move() Methoden MenuButtons , während MenuButtons 's update() animate() , etc.

Offensichtlich, wenn du magst, kannst du irgendeine Art von Sammlung anstelle von varargs für den Parameter der Drawer 's drawObjects() . Ich mag die nette Fliessheit, die durch die varargs möglich gemacht wird, da man die Kollektion nicht schaffen muss.

Für andere Tipps für die Einhaltung von Code im Einklang mit dem Gesetz von Demeter, schauen Sie sich diesen Artikel: Gesetz von Demeter und wie man damit arbeiten

Ich mag das Konzept eines ResourceManagers.
Aber ein ResourceManager sollte responsilbe für das Laden von Ressourcen, Caching und Befreiung sein.
Rendering ist definitiv eine Methode eines Renderobjekts.

So könnte die Scence-Render-Methode das Rendering danach delegieren
Instanziieren eines Renderers und Fütterung mit Drawables, wie der Renderer nicht
Renderressourcen, aber rendernde Objekte.

Sagen:

 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; } } 

Dies trennt deutlich die Logik und Verantwortlichkeiten für die während des Szenenlebenszyklus durchgeführten Aufgaben. Ein Ressourcenmanager, der für das Abrufen von Ressourcen verantwortlich ist und wie es lange Ladezeiten dauern kann, macht Dinge wie das Caching oder Freigeben in niedrigen Speicher Situationen versteckt die Details aus dem Client. Ein Renderer, der für die Darstellung der Objekte und einen Controller verantwortlich ist, der für das Verschieben der Objekte zuständig ist. Der Controller selbst kann Handler für Keyboard-Events implementieren, aber das ist nicht etwas, das für die Szene transparent sein muss. Der Renderer kann Hintergründe ein- oder ausschalten oder skalieren oder Lichteffekte einstellen, aber die Szene nennt nur die Rendermethode. Der animierte Renderer ist verantwortlich für das Starten, Rendering und Stoppen von Animationen.

Ändere das:

  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(); } 

Mit diesem:

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

ResourceManager muss nicht wissen, was in Ressourcen ist. Es kann ein Feind oder zehn sein, es kümmert sich nicht um ResourceManager.

Und so in 'Resource' können Sie:

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

Mehr als das! Du könntest die "Resource" -Entwicklung mit einer Schnittstelle ändern und du wirst für Schnittstellen programmieren und nicht für Implementierungen, was cool ist! Auf diese Weise können Sie eine "Resources" für In-Game und eine andere für Menüs, die in gleicher Weise verwendet werden: Nur ändern, zur Laufzeit, die konkreten Ressourcen werden Sie in einem Menü oder im Spiel!

Jedenfalls ist nicht immer nötig, um Demeter zu füllen.

Wie kann man sehen, dass Ihre Ressourcen nicht neu erstellt werden müssen, stattdessen verwenden sie einige Ressourcen, die nicht geladen werden können (wahrscheinlich Bilder).

Sie sollten das Bildobjekt innerhalb einer Ressourcenklasse freigeben und Ihre Objekte innerhalb einer Szenenklasse erstellen, auf dem Konstruktor der Entitäten können Sie die freigegebene Ressource erhalten, die vorinstalliert ist.

Das Android ist ein Google Android Fan-Website, Alles ├╝ber Android Phones, Android Wear, Android Dev und Android Spiele Apps und so weiter.