小小单例模式

单例模式

你熟悉这段代码吗:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class X {
private static X instance = null;

private X() {}

public static X instance() {
if (instance == null)
instance = new X();
return instance;
}

// more methods...
}

当然。这是GOF这本书里面提到的单例模式。但是我总是听到别人说我们不应该使用它。

为什么我们不应该使用它?

因为它使得我们的系统难以测试

真的吗?为什么会那样呢?

因为你不能模拟(mock)一个单例对象。

不能吗?为什么不能?

这样说吧,因为唯一能够访问私有变量的类只有单例对象自身,不暴露给外部就没办法模拟(mock)。

你知道封装和测试的规则吗?

嗯,不知道。规则是什么呢?

测试胜过封装。

这是什么意思呢?

意思就是测试赢了封装。只是为了维持封装性的话,没有测试会被限制访问某个变量。

你的意思是如果测试需要访问私有变量…

…变量不应该是私有的。对。

听起来好像不对。我的意思是,封装,呃,很重要!

测试更为重要。

等等。什么?

如果代码不能够被测试,封装性好的代码又有什么好的呢?

好,好吧,但是如果我们不得不测试单例对象呢。

看如下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class X {
static X instance = null;

private X() {}

public static X instance() {
if (instance == null)
instance = new X();
return instance;
}

// methods.
}

class TestX {
@Before
public setup() {
X.instance = new XMock();
}
}

class XMock extends X {
// overide methods
}

哦,你把实例变量变成“包”范围。

对。

这样的话你就可以模拟单例对象了。

对。考虑一下代码:

1
2
3
4
5
6
7
public class X {
public static X instance = new X();

private X() {}

// methods.
}

等等!实例方法哪去了?

我不需要实例方法。

哦,这个实例变量是公共的。你可以直接使用它。

对。

但是…但是…其他人可能重写它?

谁会做那事?

我不知道。呃,某些坏人吧。

你们团队有这种坏人吗?

没有。但是。只是感觉这样做不太安全。

这样,如果它是公共API的一部分,我同意你的观点。但是如果这段代码只会被我们项目用到那就另当别论了…

我们应该信任我们团队成员?

当然。

这样更容易模拟(mock),对吗?

当然。

如此的话我猜我们应用使用单例模式如果我们想用的话。

当然。经管大多数情况下我不想。

这一番讨论之后,你现在却想告诉我你不想使用单例模式?

是这样,我想理解为什么不适用它更为重要。

好吧,为什么你不适用单例模式?

我有时候也用。特别是在做公共APIs的时候。

你的意思是又是信任的问题?

对。在公共API中如果我想确保只有一个实例被创建时,我就会使用单例模式。

好吧,但是如果不是在做公共API的时候,但是你任然想只创建一个实例呢?

这样的话,我就直接的创建一个。


翻译自The Little Singleton

文章目录
分享到