我的同事希望在javascript中使用单例模式来引用在我们当前的应用程序中只能创建一次的对象。
相反,我想使用命名空间的全局对象来引用这些对象。
他的论点是,如果允许创建多个这些对象,则在使用单例时重构成本会降低,因为较少的代码将不得不改变。
例如,假设我们有一个带有大型中央界面元素的应用程序,就像谷歌地图中的地图一样。
我通常希望从应用程序代码访问地图,如:
Application.interface.map.zoom(10);
他希望在整个应用程序代码中访问地图,例如:
var map = new Map(); // This is invisibly a singleton
map.zoom(10);
例如,表示控制地图的按钮的ButtonsControl
类中的代码必须以某种方式访问地图以反映用户输入。它应该使用new
还是Application
?
在我们的应用程序中,我们不得不创建另一个地图实例,但是假设我们有一天会做...
我的观点是,现在阅读代码(使用new
)的成本超过了我在以后重构代码以允许多个地图时可能获得的任何好处。阅读像他这样的代码并试图理解它的作用会产生成本。
例如,在阅读ButtonsControl
中的代码时,您可能会惊恐地发现,new Map()
构造函数中正在创建ButtonsControl
,并被迫调查映射代码以确保不会发生这种情况。跟踪该问题或者必须知道new Map()
是单身是导致使用new Map()
的成本,而阅读Application.interface.Map
是显而易见的,并且不会对未来的代码读者征税
即使new Map()
语法更改为Map.get_instance()
,我的论点是使用单例实际上会导致更多的工作,如果我们选择有一天允许多个地图,因为编写的代码单例实际上表现得好像它在特定地图上工作很重要,而不是创建一个新地图并在 it 上运行。换句话说,我想知道是否一个关于是否使用单例的好测试是否围绕它的代码似乎不知道它是在一个真实的新对象还是现有的对象上运行。例如,数据库连接的规范示例通过了此测试。
我知道这些信息并不多,但......这就是为什么制定编码标准很难的原因;如果没有完美的信息,很难做出这些决定。但是,假设您今天从头开始设计谷歌地图并做出决定。我也知道这是一个非常小的观点,但由于某种原因,我直觉上非常喜欢我的版本,而我正在努力理解为什么。
您选择哪种?为什么?
答案 0 :(得分:3)
我会说两种方法都更糟糕。我同意你对“新”令人困惑的看法,但“Application.interface.map”除了过于冗长之外,是未来保证重构的噩梦。
我会避免使用任何类型的全局变量,将“big var”作为参数传递给任何需要它的人,并将其锁定在闭包中。
function initButton(map) {
someButton.onclick = function() { map.zoom() }
}
只要你使用一个Map,你的“main”函数也可以是一个闭包。
(function(map) {
// init things here
}) ( someInstanceOfMap )
当您决定使用多个地图时,您只需要重构此主要功能。
答案 1 :(得分:2)
就设计而言,语法无关紧要。如果一段代码假定只有一个映射,那么在您的应用程序需要使用两个映射的那一天,需要重写该段代码。该假设是否采用“使用Application.interface.map
获取地图”或“使用new Map
获取地图”的形式并不重要。
我同意更合适的类名称为new MapRef
或new MapProxy
,因为您正在创建对现有地图的新引用,而不是新地图。这将减轻读者的疲劳,并使代码与单例处于相同的脆弱程度(因为,再次,您创建代理,而不是地图,因此首先必须有代理地图)。但除此之外,两种解决方案在设计方面都是相同的。
设计这个的简洁方法是将地图作为参数提供给所有在其上工作的函数(直接或作为该函数可用的对象的成员)。即使您在页面上的任何给定时间从未拥有多个地图,您仍可能希望运行单元测试(可能涉及创建模拟地图)。
为了记录,您的同事的建议被称为Monostate模式,就设计的影响而言,大多相当于单身人士。
答案 2 :(得分:1)
你的同事的想法很疯狂。在构造函数中隐藏单例是一件有毒,可怕的事情。
更重要的是,当你说:
时,你是对的是否使用单例的一个很好的测试是,如果围绕它的代码似乎不知道它是在一个真实的新对象还是一个现有的对象上运行。
而且,正如你所担心的那样,只要你真的有一个单身人士,无论它是如何实现的,你都会像一个单身人士那样写作。
从好的方面来看,这听起来很有可能你只有一个单身人士。在这种情况下,YAGNI适用,你应该做现在可能工作的最简单的事情,并担心如果它需要通过它如何解决它。