My coworker wants to use a singleton patttern in javascript to refer to objects which, in our current application, can only be created once.
Conversely, I want to refer to these objects using a namespaced global object.
His argument is that if it is ever allowable to create more than one of these objects, the refactoring cost will be lower when using singletons, because less of the code will have to change.
For instance let's say we have an application with a large central interface element, like the map in Google Maps.
I generally want to access the map from the application code like:
Application.interface.map.zoom(10);
He wants to access the map throughout the application code like:
var map = new Map(); // This is invisibly a singleton
map.zoom(10);
For instance the code in a ButtonsControl
class representing the buttons controlling the map would have to access the map somehow to reflect the user input. Should it access the map using new
or Application
?
It's about as unlikely in our application that we'd have to create another instance of the map, but let's say we do someday...
My argument is that the cost now of reading code like his (using new
) outweighs whatever benefit we might gain later when refactoring that code to allow multiple maps. The cost is incurred in reading code like his and trying to understand what it does.
For instance, when reading 开发者_Python百科the code in ButtonsControl
, you might be horrified to see that, apparently, a new Map()
is being created in your ButtonsControl
constructor, and be compelled to investigate Map's code to make sure that's not really happening. Either tracking that down or already having to know that new Map()
is a singleton is what incurs the cost in using new Map()
, whereas reading Application.interface.Map
is obvious and does not tax the future reader of the code.
Even if the new Map()
syntax is changed to Map.get_instance()
, my argument was that using the singleton will actually lead to more work later, should we choose to someday allow multiple maps, because the code written around the singleton will actually behave as though it's important it work on a particular map, rather than perhaps create a new map and operate on it. In other words, I'm wondering if a good test for whether or not to use a singleton is if the code surrounding it seems agnostic of whether it's operating on a real new object or an existing object. The canonical example of a database connection, for instance, passes this test.
I know that's not much information, but... well that's why making coding standards is hard; it's tough to make those decisions without perfect information. But let's say you were designing Google Maps from scratch today and had that decision to make. I also know it's a ridiculously small point but for some reason I intuitively strongly prefer my version and I'm trying to understand just why.
Which do you choose and why?
I'd say both methods are worse. I agree with your feelings about "new" being confusing, but "Application.interface.map" besides being too verbose, is a guaranteed refactoring nightmare in the future.
I'd avoid global variables of any kind, pass the "big var" as a parameter to whoever needs it, and lock it in a closure.
function initButton(map) {
someButton.onclick = function() { map.zoom() }
}
As long as you're using a single Map, your "main" function can be a closure as well.
(function(map) {
// init things here
}) ( someInstanceOfMap )
When you decide to use multiple maps, you'll only need to refactor this main function.
As far as design goes, syntax is irrelevant. If a piece of code assumes that there's only one map, then that piece of code will need to be rewritten the day your application needs to use two maps. Whether that assumption took the form of "the map is obtained with Application.interface.map
" or "the map is obtained with new Map
" has no importance whatsoever.
I agree that a more appropriate class name would be new MapRef
or new MapProxy
, because you're creating a new reference to an existing map as opposed to a new map. This would alleviate any reader fatigue, and get the code on the same level of brittleness as the singleton (because, again, you're creating proxies, not maps, so there must be a map to proxy to in the first place). But aside from that, both solutions are equivalent in terms of design.
The clean way to design this is to provide the map as an argument to all functions that work on it (either directly, or as a member of an object available to that function). Even if you never have more than one map at any given time on your page, you might still want to run unit tests (which might involve creating mock maps).
For the record, your coworker's suggestion is known as the Monostate pattern, which is mostly equivalent to a Singleton in terms of impact on the design.
Your coworker's idea is insane. Hiding a singleton inside a constructor is a toxic, horrible thing to do.
More importantly, you're quite right when you say:
a good test for whether or not to use a singleton is if the code surrounding it seems agnostic of whether it's operating on a real new object or an existing object.
And, as you fear, as long as you really have a singleton, however it's implemented, you're going to write as if you have a singleton.
On the bright side, it sounds like there's a strong chance you'll only ever have a singleton. In which case, YAGNI applies, and you should do the simplest thing that could possibly work now, and worry about how to fix it if it ever comes to pass that you need to.
精彩评论