views:

106

answers:

2

I'd like to:

  1. Make commonly required services visible to all classes that need them,
  2. with a minimum of boilerplate, and
  3. without sacrificing testability!

It's a small project and I think DI might be overkill, but maybe I'm wrong? Anyhow, I have been focusing on the ServiceLocator pattern as described by Martin Fowler

In a client class' constructor, I have something like this:

this->db = Locator::getDb();
this->log = Locator::getLogger();

Then the rest of the class' methods access the service through those member attributes, e.g.:

this->fooModel = new fooModel(this->db);
fooItem1234 = this->fooModel->findById(1234);

However I would also like this level of visibility for "model" objects (like fooModel above) because they are accessed from several different places and there is no need to have more than one instance.

So my initial thought was to extend Locator to have a ::getFooModel() but now it seems I'm violating the Open/Closed Principle, since I'll have to modify Locator every time I introduce a new model class.

To satisfy OCP, I could use the Dynamic Service Locator (also described on Fowler's page) however I'm not totally sold on this for the same reasons as him, i.e. it's not explicit enough.

Another solution would be to just make all my models' methods static. So:

fooItem1234 = FooModel::findById(1234);

I like this because it's zero boilerplate. I can just create a new model class and start calling it from anywhere with a single line. But now the model depends on Locator to find its DB connection and I'm not sure how I feel about that. For one, if I ever needed to have two fooModels open on separate database connections, it would be a mess and/or impossible. That said, I don't actually need to do that currently so this option seems a little tempting.

Finally, there's DI. But like I said above I think it might be too much for this little project.

Conclusion: I'm a little stuck here and would appreciate some advice from the gurus of StackOverflow!

+3  A: 

Why do you think that DI is overkill for your project? DI patterns such as Constructor Injection is way simpler and cleaner that Service Locator (which I consider an anti-pattern).

I consider Service Locator to be an anti-pattern, since it is totally opaque to the user of the API which dependencies need to be in place; thus, one could easily invoke methods on your objects in a context where the Service Locator would throw, and the API gives you absolutely no clue that this is the case.

You don't need a DI Container to use DI. If just have a simple project, you can use what is known as Poor Man's DI where you wire up dependencies manually.

Mark Seemann
Yep I meant using a container when I said I thought DI would be overkill, sorry. And thanks for the answer! I take it when you say Constructor Injection you're saying I should just pass around my dbconn and logger objects into the constructors of the classes that depend on them? If so, this is actually what I was doing. Then, for some reason, I decided having to add logger to the constructor prototype of every class was bad. But now that you've made me think about it, it seems to make a lot more sense than what I've been trying to do with this ServiceLocator.
oops
Cool. Then what you'd typically end up doing is to stack or wrap dependencies. You'll probably have a lot of low-level services, but you can often wrap two or three of these in meaningful objects, and then inject only one of these instead of three low-level services. You can repeat this as many times as you'd like to keep the number of dependencies down for individual classes.
Mark Seemann
What about stacking/wrapping not-totally-related things -- like for instance the DBconn and logger from my example above -- into one object called "config" or is that bad form?
oops
If it makes sense in the domain you are modeling, it sounds like an excellent idea; if they truly are unrelated, it's not. In the end, we jump through all these hoops to make the code more maintainable, and we will only succeed in that if we make it as intuitive as possible.
Mark Seemann
OK.. I guess the definition of "related" could vary :) I was going to ask you another question, but I figured we were moving away from the original topic so I posted it here instead: http://stackoverflow.com/questions/1900562/is-this-a-sane-implementation-of-constructor-injection Please check it out if you have the time!
oops
+2  A: 

... and there is no need to have more than one instance.

You're mixing apples and oranges. The fact that you only need one instance of a class for an application, is not the same thing as it being a good idea to make that instance globally available. With DI you don't change the cardinality - there is still just one instance. What you change is the scope of variables that address said instance. There's a difference.

troelskn