构造函数中使用‘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
相关产品推荐
相关产品推荐

