You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何单例模式违反依赖倒置原则(DIP)?

为什么单例的getInstance()会违反依赖倒置原则(DIP)

先明确依赖倒置原则的核心:

  • 高层模块不应该依赖低层模块,两者都应该依赖抽象
  • 抽象不应该依赖细节,细节应该依赖抽象

我们先看最常见的单例实现和客户端调用代码:

典型单例实现

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
    
    public void doBusiness() {
        // 业务逻辑
    }
}

客户端调用

public class Client {
    public void execute() {
        Singleton singleton = Singleton.getInstance();
        singleton.doBusiness();
    }
}

问题出在哪?

客户端代码里直接写了Singleton.getInstance(),这意味着客户端必须明确依赖Singleton这个具体类,而不是某个抽象接口:

  1. 客户端的execute()方法硬编码了具体类的静态方法,它知道“单例的类型是Singleton”这个细节
  2. 如果以后需要替换这个单例的实现(比如换成另一个实现相同业务逻辑的类,或者改成非单例),客户端代码必须修改
  3. 高层的Client模块直接依赖低层的Singleton具体类,完全没有依赖抽象,这直接违反了DIP的核心要求

为什么getInstance()是根源?

getInstance()是具体类的静态方法,它不属于任何抽象接口。客户端要获取实例,就必须直接引用这个具体类——没有抽象层作为中间层,自然无法做到“依赖抽象而非细节”。

怎么改才能符合DIP?

我们可以引入抽象接口,同时把具体类的依赖从客户端中剥离:

1. 定义抽象接口

public interface BusinessService {
    void doBusiness();
}

2. 让单例实现抽象接口

public class SingletonService implements BusinessService {
    private static final SingletonService INSTANCE = new SingletonService();
    
    private SingletonService() {}
    
    // 这里返回抽象类型,而非具体类
    public static BusinessService getInstance() {
        return INSTANCE;
    }
    
    @Override
    public void doBusiness() {
        // 业务逻辑
    }
}

3. 客户端依赖抽象,通过注入获取实例

public class Client {
    private final BusinessService service;
    
    // 构造注入:客户端只知道依赖BusinessService抽象,不知道具体实现
    public Client(BusinessService service) {
        this.service = service;
    }
    
    public void execute() {
        service.doBusiness();
    }
}

4. 初始化时绑定具体实现

public class App {
    public static void main(String[] args) {
        // 只有初始化层知道具体的单例实现
        BusinessService service = SingletonService.getInstance();
        Client client = new Client(service);
        client.execute();
    }
}

这样修改后,Client这个高层模块只依赖BusinessService抽象,完全不知道具体的单例类存在;SingletonService这个低层模块依赖BusinessService抽象,符合DIP的要求。后续如果要替换服务实现,只需要修改App里的初始化代码,Client完全不用动。


内容的提问来源于stack exchange,提问作者김유담

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 22:01:07