PHP:松耦合参数化工厂。是否可以?

标签 php factory

我的需求:
我想实例化一个对象,其中新对象的类由作为工厂方法参数给出的对象确定。同时,我希望保持工厂方法松散耦合,这样当我添加新的产品类时不需要更新它。看起来像下面的代码这样的参数化工厂是一个解决方案,但与产品类紧密耦合。

我在做什么:
我正在以不同格式保存基本相同信息的数据对象之间进行转换。这些需要从一种格式转换为另一种格式,但在它们请求转换器之前我不知道数据对象属于哪个类。我知道翻译器是如何实现的,但我正在努力解决如何为给定对象选择正确的翻译器,而不进行大量的检查对象属于哪个类的问题。我想我应该使用工厂根据数据对象的实例返回兼容的翻译器。

“有效”但紧密耦合的通用代码:

class Factory {

public static function getTranslator( object $a, object $b ): Product {
    if( ( $a instanceof ClassOne && $b instanceof ClassTwo )
        || ( $a instanceof ClassTwo && $b instanceof ClassOne )
    ) {
        return new TranslatorForClassOneAndTwo( $a, $b );
    }
    if( ( $a instanceof ClassOne && $b instanceof ClassThree )
            || ( $a instanceof ClassThree && $b instanceof ClassOne ) ) {
        return new TranslatorForClassOneAndThree( $a, $b );
    }
    if( ( $a instanceof ClassTwo && $b instanceof ClassThree )
            || ( $a instanceof ClassThree && $b instanceof ClassTwo ) ) {
        return new TranslatorForClassTwoAndThree( $a, $b );
    }
    //...and so on.
}
}

用法:

    $object_a = new CrmData();
    $object_b = new CmsData();
    $object_c = new MarketingData();

//Translate object_a to object_b 
    $translator_alpha = Factory::getTranslator($object_a , $object_b);
    $translated_object_one = $translator_alpha->translate($object_a , $object_b);

    $translator_beta = Factory::getTranslator($object_a , $object_c);
    $translated_object_two = $translator_beta->translate($object_a , $object_c);

//$translated_object_one is the data of $object_a but same class as $object_b, CmsData
//$translated_object_two is the data of $object_a but same class as $object_c, MarketingData

使用上面的代码,每次添加新的 Product 类时,我都需要向此工厂方法添加一个新的案例。如果有一种方法可以基于与上述相同的逻辑来实例化这些产品,而无需显式定义每种情况,那就更好了。似乎有一种方法可以使用面向对象的结构来做到这一点,但我没有想法。另外,知道这在 php 中目前是否不可能会很有帮助,如果没有好的解决方案,我会一直使用更明确的结构。

最佳答案

另一种可能性(与我的其他答案不同,更接近你自己的答案):

  • Translator 对象响应 canTranslateBetween(Translatable, Translatable): bool (顺便说一句,这可以按照契约(Contract)定义为 PHP 中的抽象静态方法),
  • Factory 将一组翻译器类作为依赖项(构造函数参数),然后实例化其中正确的一个。

你的工厂类看起来像这样:

class TranslatorFactory
{
  /** @var string[] */
  private $translatorClasses;

  public function __construct(array $translatorClasses)
  {
    $this->translatorClasses = $translatorClasses;
  }

  public function createTranslator(Translatable $a, Translatable $b): Translator
  {
    foreach ($this->translatorClasses as $translatorClass) {
      if ($translatorClass::canTranslateBetween($a, $b)) {
        return new $translatorClass;
      }
    }
    throw new \RuntimeException('Could not find translator.');
  }
}

用法:

$translatorFactory = new TranslatorFactory([
  Translator1::class, 
  Translator2::class, 
  Translator3::class
]);

$translatable1 = new Translatable1();
$translatable3 = new Translatable3();
$translator = $translatorFactory->createTranslator($translatable1, $translatable3);

这具有在任何方面都干净且不黑客的优点(没有“魔法”或反射或类似的东西)。唯一的缺点是,在实例化工厂时必须手动列出所有翻译类,但我觉得这实际上是有道理的:毕竟,它会选择其中一个。

演示:https://3v4l.org/nfuB2

关于PHP:松耦合参数化工厂。是否可以?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59362609/

相关文章:

php - 通过 AJAX 发布的 boolean 变量在服务器端被视为字符串

java - 检查类型参数是否是特定接口(interface)

c# - 带反射 C# 的简单工厂

angularjs - 为什么这个工厂返回一个 $$state 对象而不是 response.data?

c# - 工厂模式,按属性选择

php - 使用 Composer 自动加载 tFPDF 类

php - 在谷歌地图 v3 标记集群中按下关闭按钮时,信息窗口未关闭..?

javascript - 在 Laravel 5 中通过 URL 传递数据集?

php - strip_tags 不工作

c# - 如何设计一个类来模拟未知类型?