I presume that when you say a "DateTime wrapper" what you really mean is a "Clock" interface which can be queried for the DateTime value which humans occasionally perceive as "now".
I too have encountered the problem of having to pass dependencies around a lot, and I have solved it with a universal lightweight adaptation of Domain Driven Design which I call Subject Oriented Programming.
- I don't use service locators because:
- A service locator is a mandatory global dependency. That's a bad thing to have. Trust me, you will sooner or later regret having it.
- A service locator may, as JDT pointed out, defer a compile-time error to a run-time error. And since these errors occur when a system is being wired together, while tests are usually wired differently, these errors cannot be detected with unit testing or integration testing, you have to do end-to-end testing in order to discover them.
- I also don't use dependency injection frameworks because:
- They work by magic, and I don't like magic.
- They embrace silent failure, while I mandate hard failure.
- They don't have an API that you can call, so you cannot use code completion, you have to know stuff by heart.
The idea behind Subject Oriented Programming is this:
Every object ideally belongs to a domain. In this case, it is called a subject of that domain.
A domain is the exclusive factory of its subjects. Nobody else may instantiate a subject. This in turn means that all subject constructors are hidden to the outside world. (Package-private in java.)
Ideally, subjects are visible to outside code (code outside the namespace or package of their domain) via interfaces, not as concrete classes. (But this is irrelevant to this discussion.)
Every subject receives a reference to the domain to which it belongs as its first constructor parameter. (This is analogous to how every method of an object receives the this
pointer as its first (hidden) parameter. In other words, the domain is to a subject what the object is to a method.)
Every subject that needs to use some service obtains it from its own domain via a property of the domain. (Regular getXYZ(), no map lookup.)
The domain may offer a service directly, as in myDomain.getGradientSaturator()
, or indirectly, as in myDomain.getSplineDomain().getReticulator()
.
Domains receive their dependencies as constructor parameters.
So:
Nobody needs to query any repository for services, no huge lists of dependencies are passed either to constructors or to factory methods, (except perhaps to constructors of domains, which are rare,) and the availability of all services is guaranteed (so to speak*) by the compiler.
Still, at various places where domain hierarchies are constructed, all necessary services are supplied, so any one of them can be replaced with a mock.
In lack of any better term, I am calling this Subject-Oriented Programming for the time being.
(*) so to speak, unless you do something silly, like pass null
to a constructor which expected a GradientSaturator
.