In this lesson, you will learn how to create a central "registry" that allows your game systems to find and use services without needing to know exactly where those services live or how they are constructed.
The Problem: Tight Coupling
When your PlayerController needs to access an AudioSystem, a SaveManager, or a DialogueManager, it often requires direct references. You might find yourself using FindObjectOfType<AudioSystem>() or dragging components into inspector slots. This creates "spaghetti" code where changing one system breaks everything else.
The Solution: The Service Locator
The Service Locator pattern acts as a middleman. Instead of systems looking for each other, they register themselves with a central ServiceLocator at startup. Any other system that needs a service simply asks the locator: "Give me the IAudioService."
Here is how a basic implementation looks:
public static class ServiceLocator {
private static readonly Dictionary<System.Type, object> _services = new();
public static void Register<T>(T service) => _services[typeof(T)] = service;
public static T Get<T>() => (T)_services[typeof(T)];
}
Putting it into Practice
- Define an Interface: Create an
IAudioServiceinterface. This ensures your systems depend on an abstraction rather than a concrete implementation (adhering to our previous lessons on the Dependency Inversion Principle). - Register the Service: In your
GameInitializerscript, instantiate yourAudioManagerand callServiceLocator.Register<IAudioService>(myAudioManager). - Access the Service: Anywhere in your codebase, you can now write
var audio = ServiceLocator.Get<IAudioService>();to access the functionality immediately.
Why this matters
By using this pattern, your PlayerController no longer cares if the AudioSystem is a MonoBehaviour in the scene, a plain C# class, or a mocked-up version used for unit testing. You’ve successfully decoupled the request for a service from the existence of the service itself. This makes your architecture modular, testable, and significantly easier to refactor as your project grows.
Key Takeaway: The Service Locator pattern replaces direct hard-coded references with a dynamic registry, allowing disparate systems to communicate through clean, loosely-coupled interfaces.