2013-04-10 41 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();