允许在PHP中使用抽象工厂模式进行自定义类实例化

时间:2012-02-10 00:13:21

标签: php abstract-factory

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

  

允许工厂实例化任何提交的类名   将预期的界面与工厂概念的混合相匹配?   我会更好地避免这种情况吗?

更新

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

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


相关代码......

<?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');

3 个答案:

答案 0 :(得分:1)

这是非常主观的,所以以下只是一个观点:

我不会很快使用这样的东西。如果你只有一些工厂会关心的课程,那么我只需要对它们进行硬编码。但如果你有大量这些,我认为这是合适的。

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

如果你在所有地方都使用这些技术,那么我会反对它。但由于这在实施过程中被隐藏起来,我认为你可以在做一些稍微不恰当的事情上有更多的余地。

答案 1 :(得分: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();    

答案 2 :(得分:1)

为什么不使用调度数组?即。

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"); 
    }
}