Introduction
Working with a database application often requires access to global settings that customize business rules and various functionalities. However, managing these settings can be challenging, especially for unit testing and maintaining clean code. A common problem many developers face is how to provide access to global application settings effectively, without the pitfalls of global variables. In this blog post, we’ll explore a solution using Martin Fowler’s Service Locator pattern, which allows developers to streamline access to global settings while still enabling efficient unit testing.
The Problem
When developing applications, developers create objects that perform specific tasks, such as complicated calculations. These objects often require access to global settings stored in a database. The conventional approach involves passing settings as properties to the objects when they are instantiated, typically managed by an Application Controller. While this can be a better alternative to using a global Settings
object, it poses its own challenges:
- Complex Setup: You may need to set numerous properties for each object, which can become cumbersome.
- Percolation of Properties: Properties might need to be passed down to sub-objects, complicating the architecture.
- Testing Difficulty: Unit testing might become challenging if objects rely heavily on global variables and state.
The Solution: Using the Service Locator Pattern
An effective way to tackle this problem is to implement Martin Fowler’s Service Locator pattern. This approach provides a centralized way to access your global settings while allowing for easy customization during testing.
Setting Up the Service Locator
Step 1: Create the Service Locator Class
Here’s a simple implementation of the Service Locator in PHP:
class ServiceLocator {
private static $soleInstance;
private $globalSettings;
public static function load($locator) {
self::$soleInstance = $locator;
}
public static function globalSettings() {
if (!isset(self::$soleInstance->globalSettings)) {
self::$soleInstance->setGlobalSettings(new GlobalSettings());
}
return self::$soleInstance->globalSettings;
}
}
- Explanation: The Service Locator class allows you to manage a single instance of your settings and ensures that there is only one source of truth for your application settings.
Step 2: Initialize the Service Locator in Production Code
To load the Service Locator in your production environment, you would write:
ServiceLocator::load(new ServiceLocator());
This initializes the service locator so that global settings can be retrieved throughout your application.
Mocking in Test Code
One of the key advantages of the Service Locator pattern is its flexibility for testing. In your test code, you can insert mock settings easily:
ServiceLocator s = new ServiceLocator();
s->setGlobalSettings(new MockGlobalSettings());
ServiceLocator::load(s);
- Explanation: By substituting in
MockGlobalSettings
, you can test your objects without depending on any actual database state or affecting the application’s behavior.
Conclusion
By implementing the Service Locator pattern, you can efficiently manage access to global application settings in a manner that avoids the downsides of global variables. This method simplifies the setup required for your objects while maintaining an easy pathway for unit testing.
The Service Locator pattern acts as a repository for singletons, allowing you to swap out implementations for testing purposes while keeping your code clean and organized. It’s a strategy that has been utilized successfully by many developers, and it can be an excellent addition to your application architecture.
Final Thoughts
Embracing patterns like the Service Locator not only improves the maintainability of your code but also enhances your testing strategies, leading to more robust applications. If you’re new to programming, don’t hesitate to learn and experiment with these patterns—they can save you time and effort in the long run!