In almost every project I create a few classes which implement Singleton pattern. For example, data manager - if there's some work with file system, data loader - if an application connects to the internet, different resources managers, etc. Sometimes there are up to 5-7 such classes and I start feeling that I'm doing something wrong. Is it a bad practice to use Singleton pattern too much?
问题:
回答1:
Singletons are not necessarily problems — having a single object that is an expert in its domain can be very useful — but explicitly coding the singleton-ness into the object is almost always a bad idea (and frequently is done badly too). It turns out that it is better to leave the configuration of the application — including the decision of what classes have singleton instantiations — to a separate layer that specializes in that, such as Spring for Java. Like that, the management layer can look after what is a singleton, what is a singleton within a particular context (e.g., within the scope of a session) and what always needs to be manufactured anew. That leaves you free to focus on writing the business logic.
For an example of why Singletons can be problems and why they should be singletons by management/configuration, consider a class that manages the connection to a database. Classic case for a singleton you might say. You'd be right too, right up until you find that your app has grown to the point where it has to integrate connections to two databases (it happens!) and then you've got to disentangle the whole mess. If you've kept your code agnostic of whether it is dealing with singletons or not, you stand an excellent chance of being able to handle it all by just reconfiguring; some classes will be connected to one DB and some to the other, but they'll just get on with it with minimal fuss. (Anything that needs both… well, that's why I said “excellent chance”.)
Another example of why Singletons can be problems comes when you're writing tests for your code. (You do write tests, yes?) Explicit singletons are very difficult to test, as they're hard to configure and hard to isolate. You can't tear them down properly between tests because that implies having many of them. If your app uses singletons only by configuration, a testing configuration can easily change that and you can do your tests far more easily.
回答2:
There's a lot of discussion on this topic. For some people, Singleton is considered an anti-pattern. It can be very useful however, but it tends to be much trickier to implement correctly than it seems. There are situations where its use is justified, for instance to have a unique entry point for data common to a whole application - and therein lies the tricky part, it's global state in disguise, safe for use if and only if the data is immutable.
As an aside note JEE6, the latest version of Java's Enterprise Edition, now includes support for the Singleton pattern as an EJB component. But think about it for a moment, it took six revisions of the standard to finally get it done!
回答3:
Since TDD took its place programmers learned that testing singletons is a pain and started avoiding it. The same effect can be achieved by injecting needed classes/resources such as connection manager, resource manager, etc. Going this way, in the test environment these classes can simply be mocked, injected and covered with test.
On the other hand, in some cases only using singleton seems the right way - for ensuring that only one instance exists. I.e. it is useful for connection pools for it guarantees that one and only one instance exists at the time and there will be no leaks. (Note: not all implementation of singleton pattern can really provide this. In Java the right way would be using enum - for the language itself ensures its uniqueness.)
In summary, my answer is yes, using too much singletons is bad. Consider using DI principle instead.
回答4:
Interesting article on singleton being an anti-pattern, although I don't quite agree. I've never used the singleton as a stand alone solution, I've always coupled it with the factory pattern, which I think dissuades the argument of state and encapsulation.
A good example of a singleton/factory solution is a database class. You may have several databases, each of which require their own connection, yet you don't want every call to instantiate and create a new connection. You want to recycle shared connections to avoid the 'too many connections' landmine.
Something along the lines of:
/**
* Database manager for handling database related stuff
* Does use Zend_Db and Zend_Config
*/
class Database_Manager
{
protected static $dbos = array(); // Protected scope enforces encapsulation
public static function getDbo($name)
{
// Init
$hash = md5($name);
// Attempt to use singleton
if (array_key_exists($hash, self::$dbos)) {
return self::$dbos[$hash];
}
// Your db connection settings are set here either via
// switch/case based on name, or loaded from a config file (yaml, xml, etc)
$dbConnectionParams = array('your', 'connection', 'settings');
$config = new Zend_Config($dbConnectionParams);
$dbo = Zend_Db::factory($config->database);
// Adding to singleton so can be referenced in future calls
self::$dbos[$hash] = $dbo;
return $dbo;
}
In this example, the factory ensures encapsulation, while the singleton recycles already instantiated database objects.
At the end of the day it's up to you and what you want to support down the road.