工厂模式中的Reflection API

时间:2016-01-03 11:52:00

标签: php oop design-patterns reflection factory

通常我们使用Factory方法来处理创建对象的问题,而不必指定将要创建的对象的确切类。

让我们说我们有一些记录器的5种不同的实现。这是伪代码如何使用传统的Factory方法实现:

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

使用Reflection API代替传统的Factory模式(使用if / else条件)是否有任何缺点?使用它是好的做法吗?

1 个答案:

答案 0 :(得分:2)

我对您的用例有一个建议,虽然它没有解决您的问题,但它是您可以为您的工厂采取的另一种方法。

假设您有一个配置文件来存储您要使用的实现:

# 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];
}

稍后当yu需要添加或删除任何记录器时,您只需在config中的列表中添加或删除即可。 只是尝试将LoggerInterface实现到每个记录器,这样无论您的实现如何都不会破坏您的代码。这里有一个用于记录器的PSR标准接口:PSR-3 Log

之后的PSR-3 Log Description

有了这个,我认为它提供了比switch语句更好的实现,并且不需要昂贵的反射。

Just My Two Cents