Search code examples
javadesign-patternsstate-pattern

Alternatives to field injection in state pattern


I'm developing a game and the game consists of states such as intro, menu, loading, game, etc... These states are swapped (or placed on top of each other) by the state manager which is invoked within the states themselves as each state has a reference to the manager like this:

class IntroState extends State {
    //....
    void update() {
        showIntro();
        if(done) {
            stateManager.swapState(new MenuState())
        }
    }
    //....
}

I believe that this is the "state pattern", but please correct me if I'm wrong.

Certain states have certain dependencies to things such as settings and input configuration which is sent to the game core module from platform specific modules (for example PC platform has keyboard controls while mobile has touch controls) and as such they aren't and shouldn't be static.

Initially I had those dependencies as constructor parameters and I passed them around but then I ran into cases where certain states, such as my loading state which only renders a typical loading screen while loading resources, need to have dependencies which they don't use just so they can be passed to states which do depend on them. And then the more features I added, the larger the dependency list would become.

This looked bad so I created a simple/naive field injector using reflection which works, however reflection is quite slow on android and I'm not particularly fond of reflection.

I've briefly considered Dagger2 DI framework, which doesn't use reflection and promises solid performance, but annotation generated code and heavy assembly boilerplate turned me away from it quite quickly.

I am thus looking for suggestions on how could I send/request certain dependencies for my states without constructor clutter or reflection based field injection.


Solution

  • I have also had this problem and found that the simplest solution is the best one: collect all of your game services into one class.

    So rather than this:

    State mainMenuState = new MainMenuState(inputService, renderingService, gameStateService);
    State loadingState = new MainMenuState(renderingService, gameStateService);
    // etc...
    

    Do this:

    State gameServices = new GameServices(inputService, renderingService, gameStateService);
    
    State mainMenuState = new MainMenuState(gameServices);
    State loadingState = new MainMenuState(gameServices);
    // etc...
    

    And GameServices looks something like this:

    public final class GameServices {
    
        public final InputService inputService;
        public final RenderingService renderingService;
        public final GameStateService gameStateService;
    
        public GameServices(final InputService inputService, 
            final RenderingService renderingService, 
            final GameStateService gameStateService) {
    
            this.inputService = inputService;
            this.renderingService = renderingService;
            this.gameStateService = gameStateService;
        }
    }
    

    My initial concern with this was that now every type of state can access every game service. However, this never proved to be a problem in practice; your IDE can check class usage before making large changes.

    As for cross-platform logic, simply abstract it behind interfaces. So for example, 'InputService' could have a different implementation on each platform.

    One of the hardest parts of game programming is knowing when to stop engineering. Remember, you're delivering a game, not a library. :)