开发者

How to access my singletons without using global state?

I know that Singleton pattern is bad because it uses global state. But in most applications, you need to have a single instance of a class, like a datab开发者_如何学编程ase connection. So I designed my Database object without using the singleton pattern but I instanciate it only once.

My question is, how can I access my object in the low level classes (deep in the object graph) without passing it all over the place?

Let's say I have an application controller which instanciates (ask a factory to instanciate it actually) a page controller which instaciates a User model which requires the database object.

Neither my app controller nor my page controller need to know about the database object but the User class does. How am I suppose to pass the object to it?

Thanks for your time!


Consider using a global container:

  • You register the objects that are indeed relevant to the several subsystems of the application.
  • You then request that container those objects.

This approach is very popular in dependency injection frameworks (see Symfony DI, Yadif).


Singleton is bad, no doubt about it.

In the case you describe, the database object is an implementation detail of the User object. The layers above need only know about the User, not the database object.

This becomes much more apparent if you hide the user object behind an interface and only consume that interface from the layers above.

So the page controller should deal only with the interface, not the concrete class that depends on the database object, but how does in create new instances? It uses an injected Abstract Factory to create instances of the interface. It can deal with any implementation of that interface, not only the one that relies on a database object.

Once more, you hide the page controller behind an interface. This means that the concrete implementation's reliance on the Abstract Factory becomes another implementation detail. The Application Controller only consumes the page controller interface.

You can keep wrapping objects like that like without ever needing to pass around instances. Only in the Composition Root do you need to wire all dependencies together.

See here for a related answer with examples in C#: Is it better to create a singleton to access unity container or pass it through the application?


The way I've always accomplished this is to implement a static getInstance function that will return a reference to the single instance of that class. As long as you make sure that the only way you access the object is through that method, you can still ensure that you only have one instance of the singleton. For example:

class deeply_nested_class {

public function some_function() {

$singleton = Singleton::getInstance();

}

}


There are two main objects involved in loading/saving a user using the database: the user and the repository.

You seem to have implemented the functionality on the User, but I think it belongs on the Repository. You should pass the user to the Repository to save it.

But, how do you get hold of the Repository? This is created once at the top level and passed into services that need it.

The construction dependency graph and the call dependency graph are not the same thing.


Given the example you outlined, you are almost there. You are already using a factory to instantiate your page controller, but your page controller is instantiating the users directly and as your User needs to know the database.

What you want to do is use a factory to instantiate your User objects. That way the factory can know about the database and can create User instances which know about it too. You will probably be better off making interfaces for all the dependencies, which will help with testing and will mean your code is nicely decoupled.

Create an IUserFactory which creates IUser implementations and pass this into your PageControllerFactory, then your ApplicationController only needs to know about the PageControllerFactory, it doesn't need to know anything about the IUserFactory or the database.

Then in your application start up you can create all of your dependencies and inject them in to each other through the constructors.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜