2

目前,我使用抽象工厂来允许指定自定义类名来生成请求对象。我这样做的原因是让我可以在不更改代码的情况下轻松扩展核心功能。不过,最近,我对这种方法的有效性有些怀疑。所以我的问题是:

允许工厂实例化与预期接口匹配的任何提交的类名是工厂概念的混蛋吗?我会更好地避免这种情况吗?

更新

这里的逻辑是这样的:一方面,现实生活中的汽车工厂(例如)如果没有配备制造这种汽车的机器,就无法制造汽车。另一方面,下面的代码就像给同一家汽车工厂提供蓝图来制造它原本不打算制造的定制汽车。

另一种方法是传入一个配置对象,指定可以与工厂一起使用的自定义类名,并限制工厂仅在它与配置指定的自定义类名特别匹配时才生成自定义类。有什么想法吗?


以及相关代码...

<?php

interface AbstractRequestFactory
{
  public function buildRequest($type);
}

class RequestFactory implements AbstractRequestFactory
{
  public function buildRequest($type='http')
  {
    if ($type == 'http') {
      return new HttpRequest();
    } elseif ($type == 'cli') {
      return new CliRequest();
    } elseif ($custom = $this->makeCustom($type)){
      return $custom;
    } else {
      throw new Exception("Invalid request type: $type");
    }
  }

  protected function makeCustom($type)
  {
    if (class_exists($type, FALSE)) {
      $custom = new $type;
      return $custom instanceof RequestInterface ? $custom : FALSE;
    } else {
      return FALSE;
    }
  }
}

// so using the factory to create a custom request would look like this:

class SpecialRequest implements RequestInterface {}

$factory = new RequestFactory();
$request = $factory->buildRequest('\SpecialRequest');
4

3 回答 3

1

这是相当主观的,所以以下只是一种观点:

我不会很快使用这样的东西。如果你只有少数几个工厂会关心的类,那么我只会对它们进行硬编码。但是,如果您有大量这些,我认为它可能是合适的。

鉴于您正在验证该类扩展了适当的接口,我会说您所做的事情没有任何问题,因为它是故障安全的。使用该工厂方法的代码看起来很干净;我认为这是最重要的。

如果你到处都在使用这种技术,那么我会反对它。但由于这在实现中被隐藏起来,我认为你可以有更多的余地来做一些不恰当的事情。

于 2012-02-10T00:41:26.517 回答
1

你所拥有的看起来很不错。拥有工厂的目的是传递一些标准,并让方法返回一个对象,您假设该对象将具有可用于调用代码的相同可调用方法。您通过实现 RequestInterface 来强制执行此假设,因此只要任何自定义请求类实现相同的接口,您就不会最终陷入“无法在非对象上调用函数”的情况。

一些建议(只是个人喜好):

  • 我会在 buildRequest 中的 $type 上使用 switch / case

  • 我会从 makeCustom() 返回 null 或 object,否则你正在混合返回类型(object 和 bool)

  • 根据您有多少自定义类型,我实际上会将它们硬编码到开关盒中,以减轻任何混乱。不要误会我的意思,如果你有很多课程,你所拥有的就很棒,但你很可能没有。

  • 您是否考虑过将“无需更改代码即可轻松扩展核心功能”部分放入可以通过自定义类型类扩展的抽象父类中?

  • 此外,由于工厂创建对象,因此通常将其设置为静态。

示例代码片段:

public static function getRequest($type='http')
{
    switch ($type) {

        case 'http':
            return new HttpRequest();

        case 'cli':
            return new CliRequest();

        case 'myCustom1':
            return new MyCustom1();

        case 'myCustom2':
            return new MyCustom2();

        default: 
            throw new Exception("Invalid request type: $type");
    }
}

$request = RequestFactory::getRequest($type);

// As long as all objects in factory have access to same methods
$request->doSomething();
$request->andDoSomethingElse();

// Otherwise you end up with that feared 'unable to call function on non-object'
$request->iAmASneakyMethodNotEnforcedByAnInterfaceOrAvailableByExtension();    
于 2012-02-10T00:53:50.343 回答
1

为什么不使用调度数组?IE

class RequestFactory 
{
    private static $requests = array(
      'http' => 'HttpRequest',
      'cli' => 'CliRequest',
      'summatelse' => 'Summat'
    );
    public static GetRequest($type)
    {
       if (array_key_exists($type, $requests)) return new $requests[$type];
       else throw new Exception("Invalid request type: $type"); 
    }
}
于 2012-02-10T01:02:06.460 回答