在哪里实施工厂方法?

时间:2013-04-10 14:40:39

标签: php oop factory

我一直在努力掌握OOP概念,虽然我确实掌握了大多数概念,但我经常发现自己需要一些关于实际实施的建议。其中一种情况是工厂方法。

我正在编写一个PHP应用程序,它将处理从Web和命令行界面传入的请求,因此我提出了以下简单的类继承结构来涵盖这两种类型的请求:

abstract class Request {
}

class HttpRequest extends Request {
}

class CliRequest extends Request {
}

现在我需要一个返回具体Request实例的工厂方法,具体取决于php_sapi_name()返回的值:

public function create() {
    if(php_sapi_name() === 'cli')
        return new CliRequest();
    else
        return new HttpRequest();
}

我的问题是:我在哪里放?我至少可以想到三种可能性:

1)单独一个类中的静态方法:

class RequestFactory {
    public static function create() {
        // ...
    }
}

2)单独一个类中的常规方法(需要先实例化该类):

class RequestFactory {
    public function create() {
        // ...
    }
}

3)抽象父类中的静态方法:

abstract class Request {
    public static function create() {
        // ...
    }
}

每种解决方案的优缺点是什么,哪些被认为是“正确的”以及为什么?

6 个答案:

答案 0 :(得分:1)

所有这些可能性将按预期工作。我实际上并没有得到任何“缺点”,恕我直言,你的封装目标是什么。 现在,让我们来看看Factory Method模式的本质:

  

定义用于创建对象的接口,但让那些类   实现接口决定实例化哪个类。工厂   方法允许类将实例化延迟到子类。

我不确定你愿意做的事情是否完全符合这个定义。

相反,看起来你想要实现一个名为“Simple Factory”的东西,其中实例化过程被封装到一个类中。

但是将这种方法直接放入定义“Request”对象接口的抽象类中并不是一个坏主意。

正如Nicolas所说,这是Java,C#和Cocoa领域的一种相当普遍的模式。

由于这些原因,我的选择将转到第3个选项

答案 1 :(得分:1)

所以,我有一个想法,使用method overloading做我认为你想要的事情。

class Request {

    private $request;
    private $valid = true;
    private $type;
    private $vars;
    private $methods;

    public function __construct() {
        $this->type = php_sapi_name() === 'cli' ? 'cli' : 'http';
        if($this->is_cli()) $this->request = new CliRequest();
        else if($this->is_http())  $this->request = new HttpRequest();
        else {
            $this->valid = false;
            return;
        }
        $this->vars = get_class_vars($this->request);
        $this->methods = get_class_methods($this->request);
    }

    public function __get( $var ){
        if(!$this->valid) return false;
        if(!in_array($var, $this->vars)) return false;
        return $this->request->$var;
    }

    public function __set( $var , $val ){
        if(!$this->valid) return false;
        if(!in_array($var, $this->vars)) return false;
        return $this->request->$var = $val;
    }

    public function __call( $meth, $args ){
        if(!$this->valid) return false;
        if(!in_array($meth, $this->methods)) return false;
        return call_user_func_array($this->request->$var, $args);
    }

    public function is_cli( ){
        return $this->type == 'cli';
    }

    public function is_http( ){
        return $this->type == 'http';
    }


}

// Then, when calling the function...
$request = new Request;
$request->variable; // will get the variable from the Cli or Http request class
$request->method("a","b","c"); // Will run the method from the Cli or Http request class

答案 2 :(得分:1)

要创建真正松散耦合的代码,您可以使用Ray.Di或Injektor并执行类似以下操作:

<?php

use Ray\Di\Di\Inject;
use Ray\Di\Di\Scope;

/**
 * @Scope("Singleton")
 */
abstract class Request {
}

class HttpRequest extends Request {
}

class CliRequest extends Request {
}

class ARequestConsumer {
    /* @var Request */
    private $request;

    public function __construct( Request $request ) 
    {
        $this->request = $request;
    } 

    public function foo()
    {
       //...
    }
}

class Global extends Ray\Di\AbstractModule {

    public function configure()
    {
        $this->bind( 'Request' )
            ->toProvider( 'RequestProvider' );
    }
} 

class RequestProvider implements \Ray\Di\ProviderInterface {
    /**
     * @return Request
     */
    public function get()
    {
       //.. factory method logic goes here that produces a concrete instance of Request

    }
}



$injector = Injector::create([new Global]);
$consumer = $injector->getInstance('ARequestConsumer');
$consumer->foo();

答案 3 :(得分:0)

在父类中使用静态方法对我来说似乎并不是一个糟糕的解决方案。 看看Java中的Calendar类:有一个getInstance方法(实际上很多),它根据你的Locale和其他一些标准返回一个Calendar实例。

答案 4 :(得分:0)

在这种情况下,我宁愿使用Base Abstract类作为Instance的创建者使用静态方法(第三选项)

我将使用外部类,就像在第一个选项中我需要创建一些可以打破封装的依赖,就像对不同的实现具有不同的依赖关系一样。并且会减少课程的可维护性。

答案 5 :(得分:0)

设计模式不仅限于OOP,而且许多OOP设计模式的实现都是在考虑内存管理的情况下编写的。

我来自Java世界和Java,你必须使用严格的OOP设计模式。仅仅因为Java中的所有东西都是对象。有时您必须创建一个对象和一个方法,即使模式本身实际上不需要它。 工厂方法设计模式就是这样一个例子。 通过接口设计工厂的实现是非常好的,但是您不需要类和方法来实现工厂。 设计模式的实现有时令人困惑的原因是编程语言有时需要在设计模式本身中不严格需要的实现。在工厂方法中使用方法创建类就是这样一个例子。

我的解决方案不仅仅是OOP,但PHP也不是这样,从长远来看,我认为不是关于OOP,而是关于设计模式工厂方法的最佳实现。

我认为PHP的优点在于它结合了两者的优点。它提供了坚实的OOP设计可能性,但它并没有抛弃程序编程的优点。

您只需创建如下代码:

function createRequest($pRequesttype){
  switch($pRequesttype){
    case "cli":
      $tmp = new CliRequest();
      break;
    case "http":
      $tmp = new HttpRequest();
      break;
    default:
      $tmp = new DefaultRequest();
  }
  return $tmp;
 }

始终返回默认实现来处理请求。 switch语句是以软件工程师友好的方式扩展选择数量的最佳选择。

现在让你在create function中调用php_sapi_name。我建议你把它搞定一下这个函数的实现。最好让一个函数只做一个作业,获取请求和处理请求是两个函数。创建一个createRequest函数,它有一个像我给你看过的参数。

回答你的问题: 1,2或3?呃,实际上是4。 :-)如果1,2或3?

肯定是1,因为我不想为一个简单的方法加载太多的类,但为了简化这种情况,我提出了解决方案4.

我不会使用方法2,因为在一个时刻创建一个类是没有效率的。我认为这是最佳实践,但不是最好的实际实施。如果使用此解决方案,那么请同时支持具有工厂接口的类。最好的OOP设计,但不是最佳的实际实施。

我当然不会使用方法3,因为抽象类是抽象数据对象和接口抽象行为。工厂方法是接口的抽象,而不是抽象类的抽象。