构造函数传入固定常量参数的优雅实现方案与设计模式
注:以下为用于演示问题的虚构场景,无对应真实业务。
问题描述
需要实现将像素点信息缓存到指定目录的功能。
初始实现方案
首先定义全局缓存路径常量(common.php):
define('PATH_TO_CACHE', '/var/www/cache');
像素类实现(pixel.php):
class Pixel { private int $x, $y; public function __construct(int $x, int $y) { $this->x = $x; $this->y = $y; } public function draw() { // drawing... file_put_contents(PATH_TO_CACHE.'/test.php', var_export([$this->x, $this->y], true)); } } $a = new Pixel(1,4); $b = new Pixel(4,1); $c = new Pixel(1,1);
该实现逻辑简单,但代码与全局常量PATH_TO_CACHE强耦合,灵活性差,无法灵活切换缓存路径,也不利于单元测试。
第一次重构:依赖注入改造
为了解除耦合,将缓存路径作为构造函数参数传入,同时通过readonly修饰属性保证对象不可变,实现类的完全独立:
class Pixel { private readonly int $x, $y; private readonly string $pathToCache; public function __construct(int $x, int $y, string $pathToCache) { $this->x = $x; $this->y = $y; $this->pathToCache = $pathToCache; } public function draw() { // drawing... file_put_contents($this->pathToCache.'/test.php', var_export([$this->x, $this->y], true)); } }
重构后出现新的问题:实例化Pixel时,需要每次重复传入相同的缓存路径参数:
$a = new Pixel(1,4, '/var/www/cache'); $b = new Pixel(4,1, '/var/www/cache'); $c = new Pixel(1,1, '/var/www/cache');
该方式存在严重的参数冗余问题。
约束条件
- 必须保持对象不可变特性
- Pixel类本身不能内置缓存路径常量,需支持跨项目复用
- 需要解决重复传参的冗余问题
解决方案
方案一:简单工厂模式(优先推荐,适配绝大多数场景)
通过独立的工厂类封装固定依赖的传入逻辑,工厂类持有全局统一的缓存路径配置,负责统一创建Pixel实例。该方案不需要引入额外第三方组件,完全满足所有约束条件。
实现方式如下:
- 保持原有依赖注入版本的
Pixel类完全不变,确保其独立性和不可变特性 - 新增
PixelFactory工厂类:
class PixelFactory { public function __construct( private readonly string $defaultCachePath ) {} public function create(int $x, int $y): Pixel { return new Pixel($x, $y, $this->defaultCachePath); } }
- 使用方式:
// 项目初始化阶段仅配置一次缓存路径 $pixelFactory = new PixelFactory('/var/www/cache'); // 后续创建实例无需重复传入缓存路径 $a = $pixelFactory->create(1,4); $b = $pixelFactory->create(4,1); $c = $pixelFactory->create(1,1);
该方案的优势:
Pixel类无任何硬编码配置,完全独立,可直接跨项目复用- 所有实例属性均为
readonly,完全保留对象不可变特性 - 缓存路径仅需在工厂初始化时传入一次,彻底消除参数冗余
- 灵活度高,如果特定场景需要使用其他缓存路径,只需要创建对应配置的工厂实例即可,不会互相干扰
- 非常便于单元测试,测试时可以给工厂传入测试专用的临时缓存路径,不会影响生产配置
方案二:DI容器参数绑定(适合已接入依赖注入容器的中大型项目)
如果项目已经接入成熟的DI容器(如PHP-DI、Symfony DependencyInjection等),可以直接在容器配置层面对Pixel类的构造函数参数做全局绑定,由容器自动完成缓存路径的注入,无需手动传参。
以PHP-DI为例,配置方式如下:
// 容器全局配置(项目初始化阶段执行一次) $container->setParameters([ Pixel::class => [ 'pathToCache' => '/var/www/cache' ] ]); // 后续创建实例时仅需传入x、y坐标,缓存路径由容器自动注入 $a = $container->make(Pixel::class, ['x' => 1, 'y' => 4]); $b = $container->make(Pixel::class, ['x' => 4, 'y' => 1]); $c = $container->make(Pixel::class, ['x' => 1, 'y' => 1]);
该方案不需要额外编写工厂类,但会引入对DI容器的依赖,小型无容器的项目不推荐使用。
不推荐的实现方式
- 不要在
Pixel类中定义静态属性保存缓存路径:静态属性属于类级别的全局状态,会破坏类的独立性,跨项目复用时容易出现状态残留,也违背了不可变设计的原则 - 不要回退到全局常量的实现方式:会重新引入强耦合,无法灵活切换缓存路径,也难以编写隔离的单元测试
内容的提问来源于stack exchange,提问作者John Smith
相关产品推荐
相关产品推荐

