- 单例对 OOP 特性的支持不友好
public class Order {
public void create(...) {
//...
long id = IdGenerator.getInstance().getId();
//... }
}
public class User {
public void create(...) {
// ...
long id = IdGenerator.getInstance().getId();
//... }
}
IdGenerator 的使用方式违背了基于接口而非实现的设计原则,也就违背了广义上理解的 OOP 的抽象特性。
单例类也可以被继承、也可以实现多态,只是 实现起来会非常奇怪,会导致代码的可读性变差。
-
单例会隐藏类之间的依赖关系
单例类不需要显示创建、不需要依赖参数传递,在函数中直接调用 就可以了。如果代码比较复杂,这种调用关系就会非常隐蔽。 -
单例对代码的扩展性不友好
如果我们将数据库连接池设计成单例类,显然就无法适应这样的需求变更,也就是说,单例 类在某些情况下会影响代码的扩展性、灵活性。 -
单例对代码的可测试性不友好
如果单例类依赖比较重的外部资源,比如 DB, 我们在写单元测试的时候,希望能通过 mock 的方式将它替换掉。而单例类这种硬编码式 的使用方式,导致无法实现 mock 替换。 -
单例不支持有参数的构造函数
如我们创建一个连接池的单例对象,我们没法通过参数来 指定连接池的大小。
第一种解决思路是:
创建完实例之后,再调用 init() 函数传递参数。需要注意的是,我们在使用这个单例类的时候,要先调用 init() 方法,然后才能调用 getInstance() 方法,否则代码会抛出异常。
具体的代码实现如下所示:
public class Singleton {
private static Singleton instance = null;
private final int paramA;
private final int paramB;
private Singleton(int paramA, int paramB) {
this.paramA = paramA; this.paramB = paramB; }
public static Singleton getInstance() {
if (instance == null) {
throw new RuntimeException("Run init() first.");
}
return instance;
}
public synchronized static Singleton init(int paramA, int paramB) { if (instance != null){
throw new RuntimeException("Singleton has been created!");
}
instance = new Singleton(paramA, paramB);
return instance; }
}
Singleton.init(10, 50);
// 先init,再使用 Singleton singleton = Singleton.getInstance();
第二种解决思路是:
将参数放到 getIntance() 方法中。
具体的代码实现如下所示:
public class Singleton {
private static Singleton instance = null;
private final int paramA;
private final int paramB;
private Singleton(int paramA, int paramB) {
this.paramA = paramA; this.paramB = paramB;
}
public synchronized static Singleton getInstance(int paramA, int paramB) {
if (instance == null) {
instance = new Singleton(paramA, paramB); }
return instance; }
}
Singleton singleton = Singleton.getInstance(10, 50);
第三种解决思路是:
将参数放到另外一个全局变量中。具体的代码实现如下。Config 是一 个存储了 paramA 和 paramB 值的全局变量。里面的值既可以像下面的代码那样通过静态 常量来定义,也可以从配置文件中加载得到。
public class Config {
public static final int PARAM_A = 123;
public static fianl int PARAM_B = 245;
}
public class Singleton {
private static Singleton instance = null;
private final int paramA;
private final int paramB;
private Singleton() {
this.paramA = Config.PARAM_A;
this.paramB = Config.PARAM_B;
}
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}return instance;
}
}
保证全局唯一,除了使用单例,我们还可以用静态方法来实现。
// 静态方法实现方式
`public class IdGenerator {
private static AtomicLong id = new AtomicLong(0);
public static long getId() {
return id.incrementAndGet();
}
}`
// 使用举例 long id = IdGenerator.getId();
实际上,它比单例更加不 灵活,比如,它无法支持延迟加载。
另一种方法:
// 1. 老的使用方式
public demofunction() { //... long id = IdGenerator.getInstance().getId(); //... }
// 2. 新的使用方式:依赖注入
`public demofunction(IdGenerator idGenerator) { long id = idGenerator.getId();}`
// 外部调用demofunction()的时候,传入idGenerator IdGenerator idGenerator = IdGenerator.getInsance(); demofunction(idGenerator);