PhpRiot
News Archive
PhpRiot Newsletter
Your Email Address:

More information

A Simple Alternative to Global Registry Dependency

Note: This article was originally published at Planet PHP on 7 December 2010.
Planet PHP

Anyone that has written object oriented code has had to use one class from within another class. The quick and dirty way to implement this is to simply hardcode the object instantiation within the current class, but this can cause you more hassle in the long run. Unfortunately, this method provides you with no means of overriding or changing that dependency on the fly, so future modifications and testing suffer.

The obvious solution to this hard-coding issue is to instead rely on objects being explicitly passed via a constructor and/or mutators. If your service requires a database adapter, create a setDbAdapter() method and whenever you instantiate a new service object, pass in the adapter. This solution provides an incredible amount of flexibility, but it does force you to set an adapter on every single service instantiation. For most applications, only one database adapter will ever be required, so it is tedious at best to pass in the adapter to each and every service.

This is where many developers (and frameworks) turn to a global registry. During the bootstrap process, they will configure a database adapter and store it in a singleton registry, and then their service object would retrieve the adapter from the registry. This gives you flexibility when configuring and setting your adapter, and it allows you to instantiate a new service without having to explicitly set commonly used dependencies, but you are ultimately just replacing one hardcoded object call with another. This means you are still limited in your ability to unit test the class properly, and you will have a difficult time debugging if you ever need to find out exactly when and where your database adapter was configured.

The Simple Solution

Use static methods to set a default database adapter for all services that need it but still allow database adapters to be set explicitly. In your bootstrap process, you configure your database adapter just like you would normally, then you set the adapter as the "default" adapter for all services. The service should have a getDbAdapter() method that pulls either the adapter set on that instance or, if there is no adapter set, it uses the default adapter instead.

This is what your abstract service could look like (adapted from my doctrine2 entity manager service):

namespaceAEpixa\Service; useAEpixa\Exception\ConfigException, AAAAZend_Db_Adapter_AbstractAasADbAdapter; abstractAclassAAbstractDbService { AAAAprotectedA$dbAdapterA=Anull; AAAAprotectedAstaticA$defaultDbAdapterA=Anull; AAAA AAAA/** AAAAA*ASetAtheAdefaultAdatabaseAadapterAforAallAdatabaseAservices AAAAA*A AAAAA*A@paramADbAdapterA$dbAdapter AAAAA*/ AAAApublicAstaticAfunctionAsetDefaultDbAdapter(DbAdapterA$dbAdapter) AAAA{ AAAAAAAAself::$defaultDbAdapterA=A$dbAdapter; AAAA} AAAA

Truncated by Planet PHP, read more at the original (another 8898 bytes)