Thread:Talk:Requests for comment/TitleValue/ServiceRegistry singleton/reply

I have detailed the rationale for this concrete case in the section below.

In general though: having a global registry with limited scope, which is used where injection is not possible at the moment, seems acceptable (and unavoidable). Ideally, it would only be used to bootstrap the dependency injection from a static entry point. A typical example would be a static function handling a hook:

public static final onSomeHook( $stuff ) { $someService = Registry::getDefaultInstance->getSomeService; $anotherService = Registry::getDefaultInstance->getAnotherService;

$handler = new MySomeHookHandler( $someService, $anotherService ); $handler->onSomeHook( $stuff ); }

This allows the MySomeHookHandler class to be tested in isolation, using mock implementations of the services. The code that relies on global state would be minimal. Ideally, this would be the only way the global registry would be used. However, it may also be used for B/C code, as in the case of the Title object, as in the section below.

The alternative would be to use closures to handle the hook, and have the services, or at least the registry/factory, instantiated when the hook is registered:

$registry = ...; $wgHooks['SomeHook'][] = function( $stuff ) use ( $registry ) { $someService = $registry->getSomeService; $anotherService = $registry->getAnotherService;

$handler = new MySomeHookHandler( $someService, $anotherService ); $handler->onSomeHook( $stuff ); }

The main argument against using the RequestContext as the registry (or making the registry a member of RequestContext) is the "kitchen sink" anti-pattern: if access to all services is bundled in a single object, any code that needs any service will (at least formally) depend on every service, causing a holistic knot of interdependent code. The argument is against passing the Registry, or a RequestContext, to MySomeHookHandler instead of the individual services is closely related: the Demeter Principle (ask for it, don't look for it). To any code that needs to instantiate MySomeHookHandler (e.g. a test case), it should be clear exactly what that class depends on, that is, what other classes it needs to operate. If it was asking for a "kitchen sink" registry, you will have to either provide everything, or you'd have to guess which services are actually needed. This means less isolation, and makes refactoring harder: if MySomeHookHandler is changed to require an additional dependency, adding a constructor parameter makes it easy to find and fix the code that needs to be adapted. If everything comes from the "kitchen sink" registry, it's hard to find the places where an incomplete registry might be used, so the requirements are not met (again, the prime example are unit tests).