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

构造函数中使用‘new’是否为不良实践?该规则是否跨语言通用?

关于构造函数中直接new对象的单元测试问题

Great question—this is a super common pitfall when you're getting started with unit testing, so let's break this down clearly.

首先,确认:构造函数中直接new非简单值对象确实是不良实践

你完全说对了,这种写法会导致紧耦合,直接破坏单元测试的可行性,核心原因有两点:

  • 你的类会和具体的依赖实现绑定,而非依赖抽象(比如接口/抽象类),没法替换成测试用的mock或stub。
  • 单元测试本该只验证当前类的逻辑,但现在必须初始化真实的协作对象——如果这些对象依赖数据库、网络服务或者其他外部资源,测试会变得缓慢、不稳定,甚至根本没法在隔离环境中运行。

举个直观的反面例子(Java):

// 不好的写法
public class OrderProcessor {
    private PaymentGateway gateway;

    public OrderProcessor() {
        this.gateway = new StripePaymentGateway(); // 直接new具体实现
    }

    public boolean processPayment(double amount) {
        return gateway.charge(amount);
    }
}

测试这个processPayment方法时,你没法模拟StripePaymentGateway的行为——要么得调用真实的Stripe接口(既花钱又慢),要么根本没法测试失败场景。

解决思路:依赖注入(Dependency Injection)

最常用的修复方式是把依赖通过外部传入,而非在构造函数内部创建。构造注入是最推荐的方式,因为它强制要求依赖必须提供,同时让类的依赖关系一目了然:

// 改进后的写法
public class OrderProcessor {
    private PaymentGateway gateway;

    // 依赖通过构造函数注入,依赖抽象而非具体实现
    public OrderProcessor(PaymentGateway gateway) {
        this.gateway = gateway;
    }

    public boolean processPayment(double amount) {
        return gateway.charge(amount);
    }
}

// 单元测试时可以轻松传入mock
@Test
public void testPaymentFailure() {
    PaymentGateway mockGateway = Mockito.mock(PaymentGateway.class);
    when(mockGateway.charge(anyDouble())).thenReturn(false);

    OrderProcessor processor = new OrderProcessor(mockGateway);
    assertFalse(processor.processPayment(99.99));
}

这个规则适用于所有编程语言吗?

是的!核心思想解耦依赖以支持测试是跨语言通用的,不管是静态类型还是动态类型语言都适用:

动态类型语言示例(Python)

# 不好的写法
class OrderService:
    def __init__(self):
        self.db = PostgreSQLDB()  # 直接绑定具体数据库实现

# 改进写法
class OrderService:
    def __init__(self, db):
        self.db = db

# 测试时用mock
from unittest.mock import Mock
def test_order_saving():
    mock_db = Mock()
    mock_db.save.return_value = True
    service = OrderService(mock_db)
    assert service.save_order({"id": 1}) is True

动态类型语言示例(JavaScript/Node.js)

// 不好的写法
class UserManager {
  constructor() {
    this.emailService = new SendGridEmailService();
  }
}

// 改进写法
class UserManager {
  constructor(emailService) {
    this.emailService = emailService;
  }
}

// 测试时用jest mock
test('sends welcome email', () => {
  const mockEmailService = { send: jest.fn() };
  const manager = new UserManager(mockEmailService);
  manager.sendWelcomeEmail('user@example.com');
  expect(mockEmailService.send).toHaveBeenCalledWith('user@example.com', 'Welcome!');
});

有没有例外情况?

如果依赖是无状态的纯工具类或者简单值对象(完全不依赖外部资源,也不需要模拟行为),直接new通常没问题。比如:

  • new Date()(获取当前时间,不过如果需要测试时间相关逻辑,还是建议注入时间提供者)
  • new StringUtils()(纯静态方法的工具类,无外部依赖)

但只要依赖涉及外部资源、状态或者需要在测试中模拟行为,就应该避免在构造函数中直接new。

内容的提问来源于stack exchange,提问作者Ezoela Vacca

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:17:25