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

用于注册数据提供者以处理多类型对象的设计模式选型

嘿,你的思路已经非常接近了!咱们来把这个方案用合适的设计模式打磨得更健壮、更灵活,完美匹配你需要的动态注册、类型匹配、可扩展需求。

基于策略模式+服务定位器的多数据源数据提供者方案

你的场景核心是根据对象类型匹配对应的数据获取策略,同时要支持动态扩展——这刚好是**策略模式(Strategy Pattern)搭配服务定位器(Registry/Service Locator)**的典型应用场景,咱们一步步完善你的代码:

1. 先规范核心接口

首先要统一数据提供者和目标对象的接口,避免后续扩展时出现类型混乱:

// 定义所有数据提供者必须实现的接口
interface DataProviderInterface {
    // 返回该提供者支持处理的对象类型(用类名比字符串更安全)
    public function supportsType(): string;
    // 根据目标对象获取对应数据(允许传入对象,方便后续需要对象参数的场景)
    public function fetchData(object $target): array;
}

// 可选:定义目标对象的通用接口,强制规范类型标识逻辑
interface DataTargetInterface {
    public function getDataType(): string;
}

2. 实现具体的提供者与目标对象

以你的城市、天气、人口数据为例:

// 天气数据提供者(对接第三方服务)
class WeatherDataProvider implements DataProviderInterface {
    public function supportsType(): string {
        return Weather::class;
    }

    public function fetchData(object $target): array {
        // 先做类型校验,避免传错对象
        if (!$target instanceof Weather) {
            throw new InvalidArgumentException("This provider only supports Weather objects");
        }
        
        // 调用第三方API获取对应城市的天气
        return [
            'temperature' => 28,
            'humidity' => 55,
            'condition' => 'Partly Cloudy'
        ];
    }
}

// 天气目标对象
class Weather implements DataTargetInterface {
    private int $cityId;

    public function __construct(int $cityId) {
        $this->cityId = $cityId;
    }

    public function getDataType(): string {
        return self::class;
    }

    public function getCityId(): int {
        return $this->cityId;
    }
}

// 城市基础信息提供者(对接MySQL)
class CityMetadataProvider implements DataProviderInterface {
    public function supportsType(): string {
        return City::class;
    }

    public function fetchData(object $target): array {
        if (!$target instanceof City) {
            throw new InvalidArgumentException("This provider only supports City objects");
        }
        
        // 从MySQL city表查询数据
        return [
            'id' => $target->getId(),
            'name' => 'Shanghai',
            'province' => 'Shanghai',
            'country' => 'China'
        ];
    }
}

// 城市目标对象
class City implements DataTargetInterface {
    private int $id;

    public function __construct(int $id) {
        $this->id = $id;
    }

    public function getDataType(): string {
        return self::class;
    }

    public function getId(): int {
        return $this->id;
    }
}

3. 完善DataProviderRegistry(服务定位器核心)

你的注册表已经是核心逻辑,现在优化它的健壮性和易用性:

class DataProviderRegistry {
    /** @var DataProviderInterface[] 存储类型与提供者的映射 */
    private array $providers = [];

    // 注册单个数据提供者
    public function register(DataProviderInterface $provider): void {
        $type = $provider->supportsType();
        // 可以选择覆盖已有提供者,或者抛出异常防止重复注册,根据业务需求调整
        $this->providers[$type] = $provider;
    }

    // 批量注册提供者(简化多提供者注册流程)
    public function registerMultiple(DataProviderInterface ...$providers): void {
        foreach ($providers as $provider) {
            $this->register($provider);
        }
    }

    // 根据目标对象获取对应数据
    public function getData(object $target): array {
        // 优先使用对象自身的类型标识,兼容未实现接口的对象(用类名作为 fallback)
        $type = $target instanceof DataTargetInterface 
            ? $target->getDataType() 
            : get_class($target);
        
        if (!isset($this->providers[$type])) {
            throw new RuntimeException("No data provider found for type: {$type}");
        }

        return $this->providers[$type]->fetchData($target);
    }
}

4. 实际使用示例

现在就可以像你期望的那样灵活使用了:

// 创建需要获取数据的对象
$city = new City(101);
$weather = new Weather(101);
// 后续新增人口统计对象
$demographic = new Demographic(101);

// 初始化注册表并注册所有提供者
$dataProviderRegistry = new DataProviderRegistry();
$dataProviderRegistry->registerMultiple(
    new CityMetadataProvider(),
    new WeatherDataProvider(),
    new DemographicDataProvider() // 新增提供者直接注册即可
);

// 批量获取数据并处理(比如渲染表格)
$objects = [$city, $weather, $demographic];
foreach ($objects as $object) {
    $data = $dataProviderRegistry->getData($object);
    // 这里可以把数据组装成表格行
    var_dump($data);
}

为什么这个方案适合你?

  • 策略模式:每个数据提供者都是独立的策略,负责自己的数据源逻辑(MySQL/第三方API/MongoDB),完全解耦,新增提供者只需要实现DataProviderInterface,不需要修改注册表代码。
  • 服务定位器:集中管理所有提供者,提供统一的数据获取入口,避免在业务代码中到处实例化提供者。
  • 开闭原则:后续新增任何对象类型(比如城市交通数据),只需要三步:
    1. 新建目标对象类(可选实现DataTargetInterface)
    2. 新建对应的DataProviderInterface实现类
    3. 注册到注册表中即可,完全不需要修改现有代码。

额外优化建议

  • 缓存机制:如果数据获取成本高(比如第三方API),可以在注册表或提供者中加入缓存逻辑,避免重复请求。
  • 异步支持:如果某些数据源是异步的,可以把fetchData改成异步方法,配合协程使用。
  • 多提供者优先级:如果同一个类型有多个备用数据源,可以在注册表中支持优先级排序,获取时选择最高优先级的提供者。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:06:29