2013-04-10 39 views
6

我一直在努力去理解面向对象的概念,虽然我的确了解了其中大多数概念的一般概念,但我经常发现自己需要一些关于它们实际实现的建议。其中一种情况是工厂方法。何处实施工厂方法?

我写那将处理来自Web和命令行界面传入的请求一个PHP应用程序,所以我想出了下面简单的类继承结构涵盖这两种类型的请求:

abstract class Request { 
} 

class HttpRequest extends Request { 
} 

class CliRequest extends 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() { 
     // ... 
    } 
} 

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

+1

选项2,其他甚至不OOP。 – 2013-04-10 15:10:10

+0

恕我直言,'2)'是最好的一个。但是,您应该将'php_sapi_name'作为'create'方法的参数。 – Touki 2013-04-10 16:13:17

+0

尽管所有方法都可以正常工作,但我会推荐选项1,它不需要事先通过'new RequestFactory()'实例化工厂类。 – SaschaM78 2014-01-10 11:37:57

回答

0

在父类中使用静态方法似乎没有一个可怕的解决方案,我在所有。 查看Java中的Calendar类:有一个getInstance方法(实际上有很多),它返回一个Calendar实例,取决于您的Locale和其他条件。

0

在这种情况下,我使用静态方法(第三选项)

我将在第一个选项使用外部类,像而使用抽象基类作为实例的创建者,当我需要创造一些依赖关系可以打破封装,就像为不同的实现提供不同的依赖关系一样。并且会使课程不易维护。

1

所有这些可能性将按预期工作。我没有真正得到任何“缺点”,因为它符合,什么是恕我直言,你的封装目标。 现在,让我们来看看工厂方法模式的本质:

定义的界面,用于创建一个对象,但让该 实现接口决定将哪一个类实例的类。工厂 方法让类将实例化推迟到子类。

我不确定你愿意做什么完全符合这个定义。

相反,它看起来像你想实现一些所谓的中实例化过程被封装成一个类“Simple Factory”。

但有这种方法直接到定义你的“请求”对象的界面看起来并不像一个坏主意抽象类。

正如萨科说,这是用Java,C#和可可的土地相当常见的模式。

出于这些原因,我的选择会去3选项

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 
0

设计模式不仅限于OOP,而且OOP设计模式的很多实现都是用一些内存管理思想编写的。

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

我的解决方案是不是纯粹的面向对象,但PHP是不是太,从长远来说是它不是在我看来OOP,但关于设计模式工厂方法的最佳实施。

我认为PHP的优美是它结合了两者的优点。它提供了坚实的面向对象设计的可能性,但它没有抛弃程序编程的优点。

您可以简单地这样创建代码:

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

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

现在你有你的创建函数调用php_sapi_name。我建议你把它拿出来执行这个功能。最好让一个功能只做一项工作,获取请求和处理请求是两个功能。创建一个具有像我向你展示的参数的createRequest函数。

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

绝对1,因为我不希望加载太多类的简单方法,而且要简化这种情况我已经提出的解决方案4.

我不会使用方法2,因为它是没有效率创造一个时刻的课程。我会考虑这是最佳实践,但不是最好的实际实施。如果使用这种解决方案,那么也请通过工厂界面支持该类。最好的OOP设计,但不是最好的实际实施。

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

+0

我正在查看基于标签php的最新问题列表。我不知道这个问题很久以前就被问过了。只有在回答问题后才能看到。 :-) – 2014-02-27 10:26:06

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