2016-01-03 27 views
0

通常我們使用Factory方法來處理創建對象的問題,而不必指定將要創建的對象的確切類別。工廠模式下的反射API

可以說我們有一些記錄器的5個不同的實現。下面是僞代碼,將如何與傳統的工廠方法實現:

class LoggerFactoryMethod extends AbstractFactoryMethod { 
function makeLogger($param) { 
    $logger = NULL; 
    switch ($param) { 
     case "mysql": 
      $logger = new MysqlLogger; 
     break;  
     case "mongo": 
      $logger = new MongoLogger; 
     break;  
     case "txt": 
      $logger = new TxtLogger; 
     break; 
     case "redis": 
      $logger = new RedisLogger; 
     break; 
     case "memcached": 
      $logger = new MemcachedLogger; 
     break; 
     default: 
      $logger = new TxtLogger; 
     break;  
    }  
    return $logger; 
} 

這工作得很好,但有很多的if/else(或開關/碼)的條件下是不是很爽。假設明天我們想添加一個實現。我們需要修改此代碼並添加新的案例條件。有了這個,我們可能會違反SOLID原則中的開放/封閉原則。

相反,使用Reflection API更加優雅,並且不需要對新實現進行重大更改。使用Reflection API的同一個示例應該是:

class LoggerFactoryMethod extends AbstractFactoryMethod { 
function makeLogger($param) { 
    $allowed_implementations = array('mysql','mongo', 'txt', 'redis' , 'memcached'); 
    $class = new ReflectionClass($param); 

    if (!in_array($param, $allowed_implementations) || !($class->IsInstantiable())){ 
     return null; 
    } 

    return $class->newInstance(); 
} 

使用反射API代替傳統工廠模式(使用if/else條件)是否有任何缺點?使用它是好的做法嗎?

+3

我無法PHP說話,但「一般」(其他語言,反射通常比構造更貴,所以你是一個資源開銷造成的),再加上你實際上是限制你的工廠模式,因爲你假定構造函數都是空操作的,如果你有一些操作數,那麼你需要邏輯來啓用它。在一般情況下,工廠擁有if/then/else或某種動態綁定的長列表,但if/then或switch stmt通常更易於處理+更快速且更易於調試(以更多物理代碼爲代價)因此(忽略任何PHP發佈它的權衡'?') – mawalker

+0

我同意@mawalker,並希望傳統的交換機實現。當你想解決簡單的條件時,簡單的條件語句沒有任何問題。由於$ allowed_implementations,當添加新的記錄器實現時,您建議改進的工廠方法也需要修改。 – iluwatar

+0

@iluwatar指出,如果你想超級靈活並支付**反射成本**,只需提取'$ allowed_implementations'集合,以便將其配置爲系統/應用程序屬性,那麼您的工廠將始終可以找到它。 –

回答

1

我對您的使用案例有一個建議,雖然它不能解決您的問題,但它是您可以爲您的工廠採取的另一種方法。

假設你有一個配置文件來存儲你想用什麼實現:

# path/to/config.php 
<?php 
return [ 
    'mysql' => Some\Namespace\MysqlLogger::class, 
    'mongo'=> Some\Namespace\MongoLogger::class, 
    'txt' => Some\Namespace\TxtLogger::class, 
    'redis' => Some\Namespace\RedisLogger::class, 
    'memcached' => Some\Namespace\MemcachedLogger::class, 
]; 

的,工廠會根據參數傳遞,並根據配置初始化採取那些創造記錄。

class LoggerFactoryMethod extends AbstractFactoryMethod { 
protected $logger = []; 
public function __construct() 
{ 
    $this->logger = require('path/to/config.php'); 
} 

function makeLogger($param = 'txt') { 
    return new $this->logger[$param]; 
} 

後來,當宇需要添加或刪除任何記錄,你可以只添加或從配置列表中刪除。 只要嘗試爲每個記錄器實現LoggerInterface,那麼無論您的實現如何,都不會破壞您的代碼。這裏有一個用於記錄器的PSR標準接口:PSR-3 Log繼此之後PSR-3 Log Description

有了這個,我認爲它提供了比switch語句更好的實現,並且不需要昂貴的反射。

只是我的兩分錢