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

如何重构依赖过多的构造函数?DI新手的实际问题咨询

嘿,作为DI新手遇到这种依赖差异巨大的情况确实挺头疼的,我来给你梳理下问题和解决方案~

问题分析

你现在的Paypal类其实违反了单一职责原则——它同时承担了“创建支付”和“处理PayPal API端点”两个完全不同的职责,导致依赖变得臃肿:一个方法需要9个依赖,另一个只需要1个,不仅让类的逻辑混乱,测试起来也得mock一堆用不上的依赖,非常麻烦。

解决方案1:按职责拆分类(推荐)

最彻底的解决办法是把两个职责拆成独立的类,每个类只保留自己核心职责所需的依赖,这样依赖关系会清晰很多:

拆分后的支付创建类

class PaypalPaymentCreator {
    private $restAPIClient;
    private $db;
    private $logger;
    private $paypalGateway;
    private $paymentGateway;
    private $ordersGateway;
    private $usersGateway;
    private $resourcesGateway;
    private $configGateway;

    public function __construct(
        RestAPIClient $restAPIClient,
        Db $db,
        Logger $logger,
        PaypalGateway $paypalGateway,
        PaymentGateway $paymentGateway,
        OrdersGateway $ordersGateway,
        UsersGateway $usersGateway,
        ResourcesGateway $resourcesGateway,
        ConfigGateway $configGateway
    ) {
        $this->restAPIClient = $restAPIClient;
        $this->db = $db;
        $this->logger = $logger;
        $this->paypalGateway = $paypalGateway;
        $this->paymentGateway = $paymentGateway;
        $this->ordersGateway = $ordersGateway;
        $this->usersGateway = $usersGateway;
        $this->resourcesGateway = $resourcesGateway;
        $this->configGateway = $configGateway;
    }

    public function createPayment() {
        // 原来的createPayment逻辑,安心使用所有依赖
    }
}

拆分后的API端点处理类

class PaypalApiEndpointHandler {
    private $restAPIClient;

    public function __construct(RestAPIClient $restAPIClient) {
        $this->restAPIClient = $restAPIClient;
    }

    public function paypalApiEndpoint() {
        // 原来的paypalApiEndpoint逻辑,只需要这个单一依赖
    }
}

这样拆分后,每个类的职责明确,依赖精准:测试PaypalApiEndpointHandler时只需要mock一个RestAPIClient,而不用管其他8个依赖;后续维护时也不会因为修改一个职责的逻辑影响到另一个,完全符合SOLID原则。

解决方案2:方法注入(临时过渡方案)

如果暂时不想大动干戈拆分类,可以用方法注入的方式,把paypalApiEndpoint需要的依赖直接作为参数传入,而不通过构造函数注入:

class Paypal {
    // 保留原来的构造函数和所有依赖(供createPayment使用)
    private $restAPIClient;
    private $db;
    private $logger;
    private $paypalGateway;
    private $paymentGateway;
    private $ordersGateway;
    private $usersGateway;
    private $resourcesGateway;
    private $configGateway;

    public function __construct(
        RestAPIClient $restAPIClient,
        Db $db,
        Logger $logger,
        PaypalGateway $paypalGateway,
        PaymentGateway $paymentGateway,
        OrdersGateway $ordersGateway,
        UsersGateway $usersGateway,
        ResourcesGateway $resourcesGateway,
        ConfigGateway $configGateway
    ) {
        $this->restAPIClient = $restAPIClient;
        $this->db = $db;
        $this->logger = $logger;
        $this->paypalGateway = $paypalGateway;
        $this->paymentGateway = $paymentGateway;
        $this->ordersGateway = $ordersGateway;
        $this->usersGateway = $usersGateway;
        $this->resourcesGateway = $resourcesGateway;
        $this->configGateway = $configGateway;
    }

    public function createPayment() {
        // 原来的createPayment逻辑
    }

    // 把依赖作为参数传入方法
    public function paypalApiEndpoint(RestAPIClient $restAPIClient) {
        // 使用传入的$restAPIClient处理逻辑
    }
}

不过要注意,这种方式只是临时过渡手段——它并没有解决类职责混乱的根本问题,长期来看还是拆分类更利于维护。

一些DI最佳实践

  • 优先遵循单一职责原则:类的职责越少,依赖越精简,DI的优势才能体现出来。
  • 构造函数注入核心依赖:类的大部分方法都需要用到的依赖,用构造函数注入;单个方法特有的依赖,考虑方法注入或者拆分类。
  • 避免“上帝类”:不要让一个类承担太多职责,否则会变成依赖一大堆的“上帝类”,维护和测试成本都会飙升。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:42:28