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

构造函数传入固定常量参数的优雅实现方案与设计模式

注:以下为用于演示问题的虚构场景,无对应真实业务。

问题描述

需要实现将像素点信息缓存到指定目录的功能。

初始实现方案

首先定义全局缓存路径常量(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实例。该方案不需要引入额外第三方组件,完全满足所有约束条件。
实现方式如下:

  1. 保持原有依赖注入版本的Pixel类完全不变,确保其独立性和不可变特性
  2. 新增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);
    }
}
  1. 使用方式:
// 项目初始化阶段仅配置一次缓存路径
$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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 21:03:57