简体   繁体   中英

How multiple objects can send a notification to an object without having its instance reference (in java)?

I'm developing a videogame in Java, and I want to go to the next level when all enemies are dead. I have a class Enemy, a class Level, and a class WorldGame. In WorldGame I remove enemies, but I don't know how to notify the level when it has to go to the next level when all enemies are dead without passing the instance of Level to all enemies. Do you have any suggestions?

class Alien {

}

class GameWorld {
  removeEnemy();
}

class Level {
  goToNextLevel();
}

I think you are mixing up responsibilities. A Level (or an Enemy) doesn't know about other Enemies. I think you rather needs something like this:

 class GameWorld { 

    public void mainGameLoop() {
       ...
       removeEnemiesThatRequireRemoval();
       if (getNumberEnemiesLeft() == 0) {
         currentLevel = levelsManager.goToNextLevel(currentLevel);

Meaning:

  • the level class doesn't know about the "next" level (it only knows about itself)
  • as said: a single enemy doesn't know about (all remaining) enemies

That is all knowledge that your central "control structure" needs to have. It shouldn't go in any other place.

That should be your central theme: any object should know exactly what it needs to do its core job, not more. And its core job should be as crisp and specific as possible, too.

Check out the Observer Design Pattern. The built in version in Java is depricated but you could easily write your own. Listeners (of events) are still an example of the pattern in Java.

The idea is that a Java object, say A, can be "observed" by another Java object, say O. In fact, there can be any number of "observers". All observers are independent and do not know about each other and A has no clue about the observers.

Now, what A can do is issue (via a call to a method) a notification whenever there is reason to do so (A is perhaps a monster that dies). This results in a call being made to a special method (update) in all observers, that can then act (for instance, decrement the number of still alive monsters).

The point is the once it all has been setup, no class needs references to the other classes.

Sound like you are looking for something like this.

In your example, you may use an event listener mechanism. Create an event AllEnemiesDeadEvent and a listener interface AllEnemiesDeadListener . Keep the registry of the listeners in the GameWorld . Let the Gameworld keep track of dead enemies since it calls removeEnemy() . When all enemies are dead it will raise the event.

Now, make the Level class register the AllEnemiesDeadListener and get notified when that event occurs.

In case Level is the only class which will be interested in this event the keep it simple. Just call Level#gotoNextLevel() from GameWorld and do not for event model.

Assuming the GameWorld class in your application has access to all the levels and the levels in turn has the enemies on that level. You can refer the below codebase and come up with a solution which best suits your needs.

public class GameWorld {

    private List<GameLevel> gameLevels;
    private GameLevel currentLevel;

    public GameWorld() {
        gameLevels = new LinkedList<GameLevel>();
        this.instantiate();
    }

    private void instantiate() {
        // instantiate game adding levels to it
    }

    public void moveToNextLevel() {
        currentLevel = currentLevel.next();
    }

}

public class Enemy {

    private EnemyType enemyType;
    private GameLevel gameLevel;

    public Enemy(GameLevel level, EnemyType type) {
        enemyType = type;
        gameLevel = level; 
    }

    private void create() {
        // .. instantiation task
        gameLevel.listEnemy(this);
    }

    private void killEnemy() {
        // clean up task
        gameLevel.delistEnemy(this);
        // .. clean up task
    }

}

public class GameLevel {

    private GameWorld gameWorld;
    private List<Enemy> enemies;
    private GameLevel nextLevel;

    public GameLevel(GameWorld world) {
        enemies = new ArrayList<Enemy>();
        gameWorld = world;
    }

    public void listEnemy(Enemy enemy) {
        enemies.add(enemy);
    }

    public void delistEnemy(Enemy enemy) {
        enemies.remove(enemy);
        if(enemies.isEmpty()) {
            gameWorld.moveToNextLevel();
        }
    }

    public void setNextLevel() {
        // set next level after this
    }

    public GameLevel next() {
        return nextLevel;
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM