通常我们使用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条件)是否有任何缺点?使用它是好的做法吗?
答案 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