Skip to content

Service Locator

acoox edited this page Apr 23, 2021 · 6 revisions

This game uses the Service Locator pattern to provide global access to some key services. ServiceLocator (code) itself is a static class which can be accessed directly. For example:

GameTime gameTime = ServiceLocator.getTimeSource();
PhysicsService physics = ServiceLocator.getPhysicsService();

Accessible services are:

  • Entity Service: Keeps track of all entities that exist in the game, updating them each frame. This has to be global so that each new entity can register itself.
  • Physics Service: Keeps track of physics-enabled entities, updating them according to the physics simulation each frame.
  • Render Service: Keeps track of rendered entities, drawing them each frame.
  • Resource Service: Provides a way to load and access assets from anywhere, while ensuring that the same asset is not loaded more than once.
  • Input Service: Provides access to user input.
  • Time Source: The recommended way to get the current time or calculate how much time has passed. The time source can be mocked for unit tests to prevent tests being time-dependant.

Behind the Scenes

Why not just use singletons?

Singletons are generally advised again and considered as anti-patterns, due to their drawbacks:

  • Providing global access to classes around the code base decreases readability and leads to bugs. Any function can have the side effect of modifying external state. Service locator still has this problem, but limits which classes are globally accessible and restricts access through 1 place.
  • Singletons encourage excessive coupling to global components.
  • We don't necessarily want to restrict ourselves to a single instance of a class. For example, we might be tempted to use a singleton for the game's input. If we then want to add support for local multi-player, we have to refactor code all over the code base where the singleton was used.
  • When 'lazy-loaded' (i.e. instantiated on first access rather than at the start of application), we give up some control of the lifecycle of that class. Service locator instead lets us choose exactly when we want to initialize our services.

Why not use dependency injection?

Dependency injection (DI) is a great way to keep code decoupled without relying on global instances, and avoids many of the problems listed above. However, DI can be difficult to learn and understand, since inversion of control goes against the intuitive ideas of abstraction and encapsulation. You are highly encouraged to use this pattern in the future, but for simplicity it is not heavily used in the game (other than when exposing dependencies for unit testing).

Further Reading

Table of Contents

Home

How to Play

Introduction

Game Features

Main Character

Enemies
The Final Boss

Landscape Objects

Shop
Inventory
Achievements
Camera

Crystal

Infrastructure

Audio

User Interfaces Across All Pages
Juicy UI
User Interfaces Buildings
Guidebook
[Resource Management](Resource-Management)
Map
Day and Night Cycle
Unified Grid System (UGS)
Polishing

Game Engine

Getting Started

Entities and Components

Service Locator

Loading Resources

Logging

Unit Testing

Debug Terminal

Input Handling

UI

Animations

Audio

AI

Physics

Game Screens and Areas

Terrain

Concurrency & Threading

Settings

Troubleshooting

MacOS Setup Guide

Clone this wiki locally