2012-02-10 67 views
2

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

是让工厂实例任何提交的类名 工厂概念的私生子一致预期的接口? 我会更好地避免这种情况吗?

UPDATE

这里的逻辑是这样的:一方面是,现实生活中的汽车厂(例如)无法创建一个车,如果它没有配备机械制造那种车。另一方面,下面的代码就像给同一个汽车制造厂制作蓝图,以制造它最初不打算制造的定制汽车。

另一种方法是在配置对象,指定了可以与工厂中使用的自定义类名来传递和工厂限制到产生的自定义类,只有当它的配置指定的自定义类名专门匹配。有什么想法吗?


及相关代码...

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

你可以发布$ type代码片段吗? – 2012-02-10 00:33:31

+0

@MikePurcell根据您的要求更新了代码的底部 – rdlowrey 2012-02-10 00:36:35

+0

我会减少整个界面工厂和不同类型的“CLI”,“Http”工厂实现接口 – 2012-02-10 00:43:43

回答

1

你有什么看起来不错。有一个工厂的要点是要传入一些标准,并让该方法返回一个对象,您认为该对象将具有与调用代码相同的可调用方法。你通过实现RequestInterface来强制实现这个假设,所以只要任何自定义请求类实现相同的接口,就不会以'无法在非对象上调用函数'场景结束。

一对夫妇的建议(只是个人喜好):

  • 我会在$类型使用开关/情况buildRequest

  • 我会返回null或makeCustom()对象,否则,混合返回类型(对象和布尔)

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

  • 你有没有考虑过把“轻松扩展核心功能而不改变代码”放到一个抽象的父类中,可以通过自定义类型类来扩展?

  • 另外,因为工厂创建对象,所以通常将其设置为静态。

实施例的代码片断:

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();  
+0

hehe,+1 for *“iAmASneakyMethodNotEnforcedByAnInterfaceOrAvailableByExtension()”*和有用的项目符号点 – rdlowrey 2012-02-10 00:56:31

+0

没有问题。正如我所提到的,他们只是个人喜好,没有任何反对你已经拥有的东西。 – 2012-02-10 01:06:10

1

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

我也不会很快使用这样的事情。如果你只有少数几个工厂将会关心的类,那么我只需要对它们进行编码。但是,如果你有一大堆这些,我认为这可能是适当的。

鉴于您正在验证该类扩展了适当的接口,我会说您在做什么没有问题,因为它是自动防故障的。使用该工厂方法的代码将显得干净;我认为这是最重要的。

如果你在这个地方使用这样的技术,那么我会反驳它。但是,由于这是隐藏在实施中,我认为你可以有更多的余地做一些不适当的事情。

+0

好的建议。是的,这是一个主观的问题,我几乎没有在这里发布它的原因。这与我想的是一样的,但我想要其他意见。我认为这对于像你所说的那样*是“隐藏在实现中”是有帮助的。在任何地方使用它都很难防守。 – rdlowrey 2012-02-10 00:45:54

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

不错。这也会起作用。 – 2012-02-10 01:05:31