假设我有一个加载和解析配置文件的类。但是,该配置文件可以是3种类型:
- JSON
- YAML
- INI
如何设计我的"configLoader"为了保持东西:
- 松散耦合的
- 易于单元测试
- 易于切换组件(例如,将旧解析器更改为新的更好的解析器)
但同时:
- 易于扩展(我指的是添加新类型的可接受文件,例如XML)
- 没有广泛的构造函数或设置器,以使类工作
- 所有解析器实现相同的接口(ParserInterface)
- 可以作为知名解析器(如Symfony/Yaml)的包装器(适配器),例如
- 这个类在DI容器初始化之前被初始化(在逻辑中,这里加载的值实际上会被注入到DI容器中),所以在这里使用它不是一个选项
<标题> 代码
到目前为止我写的是:
class Loader
{
/**
* Loads a configuration string from a ConfigFile
*
* @param ConfigFile $configFile
* @throws ConfigException if the file type is not recognised
* @return Config ArrayObject with configuration settings
*/
public function loadFromFile(ConfigFile $configFile)
{
// Finds out what type of config file it is
$type = $configFile->getType();
//Loads the file into memory
$configString = $configFile->read();
switch ($type) {
case ConfigFile::TYPE_YAML:
$parser = new YAML'YamlParser(); //Here's the problem
break;
case ConfigFile::TYPE_JSON:
$parser = new JSON'JsonParser(); //Here's the problem
break;
// (...) same for other file types
// Default case if file type is not recognised
default:
throw new ConfigException('Config File type not recognised');
}
return $parser->decode($configString);
}
}
标题>
如何:
interface ParserInterface{
public static test(ConfigFile $configFile);
// other required methods
}
你的加载器方法:
$classes = get_declared_classes();
foreach($classes as $class){
$class = new 'ReflectionClass($class);
if(!$class->implementsInterface('ParserInterface'))
continue;
$parser = $class->getName();
if(!$parser::test($configFile))
continue;
$parser = $class->newInstance();
return $parser->decode(...);
}
throw new Exception(...);
你刚刚"反转控制"的点,配置文件本身决定什么解析器得到使用
我会做很多像Symfony配置组件。实际上,我可能会直接用它