Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ServiceLocator and the Open/Closed Principle

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!

like image 216
oops Avatar asked Dec 14 '09 06:12

oops


People also ask

What is meant by the open-closed principle?

In object-oriented programming, the open–closed principle states "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification"; that is, such an entity can allow its behaviour to be extended without modifying its source code.

What is open close principle with example?

The Open-Close principle (OCP) is the O in the well known SOLID acronym. A module will be said to be open if it is still available for extension. For example, it should be possible to add fields to the data structures it contains, or new elements to the set of functions it performs.

Why is the open-closed principle OCP important?

Open/closed principle is intended to mitigate risk when introducing new functionality. Since you don't modify existing code you can be assured that it wouldn't be broken. It reduces maintenance cost and increases product stability. Indeed, this is what OCP is about.

Which design patterns follow open-closed principle?

For example, the Decorator pattern offers us to follow the Open Close principle. Furthermore, we may use the Factory Method, Strategy pattern and the Observer pattern to design an application with minimum changes in the existing code. That's all about 'SOLID Principles : The Open Closed Principle'.


1 Answers

Why do you think that DI is overkill for your project? DI patterns such as Constructor Injection is way simpler and cleaner than 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.

like image 59
Mark Seemann Avatar answered Oct 01 '22 00:10

Mark Seemann