单例模式(中):单例模式的弊端

本文讨论了单例模式在支持OOP特性、代码扩展性、可测试性及参数化构造方面存在的问题,并提出了三种解决思路:通过init()方法传递参数、在getInstance()中传递参数或使用全局变量。此外,还提到了静态方法作为替代方案的局限性,并引入了依赖注入以提高代码灵活性。
摘要由CSDN通过智能技术生成
  1. 单例对 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 的抽象特性。

单例类也可以被继承、也可以实现多态,只是 实现起来会非常奇怪,会导致代码的可读性变差。

  1. 单例会隐藏类之间的依赖关系
    单例类不需要显示创建、不需要依赖参数传递,在函数中直接调用 就可以了。如果代码比较复杂,这种调用关系就会非常隐蔽。

  2. 单例对代码的扩展性不友好
    如果我们将数据库连接池设计成单例类,显然就无法适应这样的需求变更,也就是说,单例 类在某些情况下会影响代码的扩展性、灵活性。

  3. 单例对代码的可测试性不友好
    如果单例类依赖比较重的外部资源,比如 DB, 我们在写单元测试的时候,希望能通过 mock 的方式将它替换掉。而单例类这种硬编码式 的使用方式,导致无法实现 mock 替换。

  4. 单例不支持有参数的构造函数
    如我们创建一个连接池的单例对象,我们没法通过参数来 指定连接池的大小。
    第一种解决思路是:
    创建完实例之后,再调用 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);

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值