策略设计模式switch分支实例化策略时依赖过多的优化方案
策略模式嵌套依赖问题解决方案
问题场景
使用策略模式时,在上下文类的switch分支中手动实例化策略类,需要逐层手写所有嵌套依赖的实例化逻辑,代码耦合度高、维护性差,新增/修改依赖时需要改动上下文类代码,还容易引发循环依赖问题。
现有CreditService代码:
class CreditService { public function addCredit($transaction) { $creditContext = new CreditContext(); $creditStrategy = $creditContext->setStrategy($transaction->transaction_type); $creditStrategy->add($transaction->id); } }
现有CreditContext代码,存在大量手动实例化嵌套依赖的逻辑:
class CreditContext { /** * @param $strategy * @return InvoiceStrategy|UserStrategy * @throws \Exception */ public function setStrategy($strategy) { switch ($strategy) { case User::class: return new UserStrategy( new EmployerTransactionService(new TransactionService(dependencies..),dependencies..); case Proposal::class: case Milestone::class: return new InvoiceStrategy( new TransactionService( new PaymentService(new PaymentRepository(), new CreditRepository(), new TransactionRepository()), new TransactionRepository(), new CreditRepository(), new IncomeReportService(new IncomeReportRepository()), new CreditService()) ); default: throw new \Exception('not found strategy'); } } }
整洁实现方案
核心实现思路:依赖注入容器(DI)+ 策略自匹配 + 集合注入,全程无需手动传递任何嵌套依赖,同时移除硬编码switch逻辑。
1. 统一策略接口约束
定义所有策略的公共接口,增加supports方法让策略自身声明支持的交易类型,不再由上下文做硬编码映射:
interface CreditStrategyInterface { public function add(int $transactionId): void; public function supports(string $transactionType): bool; }
每个策略类仅需在构造函数声明自身依赖,所有依赖由DI容器自动解析注入,不需要手动实例化:
class UserStrategy implements CreditStrategyInterface { // 直接声明需要的依赖,无需手动传入嵌套实例 public function __construct( protected EmployerTransactionService $employerTransactionService ) {} public function supports(string $transactionType): bool { return $transactionType === User::class; } public function add(int $transactionId): void { // 具体业务逻辑 } }
class InvoiceStrategy implements CreditStrategyInterface { // 无论依赖嵌套多少层,都只需要声明依赖本身,容器自动完成实例化 public function __construct( protected TransactionService $transactionService, protected IncomeReportService $incomeReportService ) {} public function supports(string $transactionType): bool { return in_array($transactionType, [Proposal::class, Milestone::class]); } public function add(int $transactionId): void { // 具体业务逻辑 } }
2. 改造上下文类,移除switch和手动实例化逻辑
上下文类不再负责实例化策略,而是通过DI注入所有策略实例的集合,运行时遍历匹配对应策略即可:
class CreditContext { /** * 注入所有实现了CreditStrategyInterface的策略实例 * 所有策略的依赖都由容器提前自动解析完成,不需要手动处理 */ public function __construct( protected iterable $strategies ) {} /** * @throws \Exception */ public function getStrategy(string $transactionType): CreditStrategyInterface { foreach ($this->strategies as $strategy) { if ($strategy->supports($transactionType)) { return $strategy; } } throw new \Exception('not found strategy'); } }
3. 改造业务服务类,通过依赖注入获取上下文
不要在业务方法中手动new上下文实例,同样通过构造函数注入,由容器管理所有实例的生命周期:
class CreditService { public function __construct( protected CreditContext $creditContext ) {} public function addCredit($transaction) { $creditStrategy = $this->creditContext->getStrategy($transaction->transaction_type); $creditStrategy->add($transaction->id); } }
4. 容器配置(以主流PHP框架为例)
现代PHP框架(Laravel、Symfony等)的DI容器都支持接口实例集合注入,以Laravel为例,仅需在服务提供者中添加几行绑定配置:
// 在AppServiceProvider的register方法中添加配置 // 给所有策略类打标签 $this->app->tag([UserStrategy::class, InvoiceStrategy::class], 'credit.strategies'); // 绑定CreditContext依赖,注入所有打过标签的策略实例 $this->app->bind(CreditContext::class, function ($app) { return new CreditContext($app->tagged('credit.strategies')); });
方案优势
- 零手动依赖传递:所有类的依赖不管嵌套多少层,都由DI容器自动解析注入,不需要写任何嵌套new的代码
- 符合开闭原则:新增策略时只需要新增对应策略类、实现接口、加入标签配置即可,完全不需要修改上下文类和业务服务的代码
- 解决循环依赖问题:所有实例由容器统一管理生命周期,单例模式复用实例,避免原代码中
CreditService和InvoiceStrategy互相实例化导致的无限递归问题 - 代码维护成本大幅降低:依赖变更时仅需修改对应类的构造函数声明,不需要逐层修改实例化逻辑
内容的提问来源于stack exchange,提问作者sina
相关产品推荐
相关产品推荐

