我们假设您有一个名为' Parser'它充当其他类的管理器,每个类都与不同类型的解析器相关。我希望能够调用主Parser类并让它与特定的解析器提供程序进行交互。
例如:
$response = $this->Parser->setProvider('ParserA')->setTemplate($template)->parse($data);
这是我目前主要的Parser课程,但我觉得有更好的方法,我也遇到了限制:
class Parser
{
private $provider;
public $template;
private function callMethod($provider, $method, $data)
{
$response = false;
$class = 'ServiceProviders\Parser\\'.ucfirst($provider);
if(method_exists($class, $method)) {
$classInstance = new $class;
$response = $classInstance->$method($data);
} else {
throw new \Exception('INVALID METHOD: '.$class.'->'.$method);
}
return $response;
}
public function setProvider($provider)
{
$this->provider = $provider;
return $this;
}
public function setTemplate($template)
{
$this->template = $template;
return $this;
}
public function parse($data)
{
$result = null;
try {
$result = $this->callMethod($this->provider, __FUNCTION__, $data);
} catch(\Exception $e) {
throw new \Exception('There was a problem calling the parser: '.$e->getMessage());
}
return $result;
}
}
每个解析器提供程序ParserA,ParserB都有解析方法。我采用这种方法的问题是ParserA需要一个模板而ParserB没有。
我是OCD,我不想将传递给解析函数的参数转换为数组。虽然简单的解决方案是这样做:
$parameters = array('data' => $data, 'template' = null);
callMethod($provider, $method, $parameters);
我想要做的是使用setTemplate()方法在主Parser类中为ParserA设置$ template值。
我已尝试过一些事情来做这件事。我尝试将Parser类转换为接口,但这并没有解决任何问题(或者我没理解它)。我尝试使ParserA扩展Parser,但后来我无法调用ParserA的特定parse()方法,因为它随后成为了父类的方法。
有没有办法实现这一点,我忽略了?
答案 0 :(得分:0)
我认为你的方法稍微偏离了一下:Parser与ParsingHandler不同,并且将两者混为一谈可能没有帮助。您的要求是经典Strategy Pattern内容。
您的Parser应该提供解析功能,但它不应该知道解析实际需要什么。把它交给ParsingHandler(在我的例子中,下面)。 ParsingHandler可以做任何喜欢的事情,前提是它提供了一个Parser将调用的方法来进行实际的解析:在我的情况下parse()
。
这是一个骨架:
class Parser {
private $parsingHandler;
function __construct($parsingHandler){
$this->parsingHandler = $parsingHandler;
}
function parse(){
return $this->parsingHandler->parse();
}
}
class TemplatedParsingHandler {
private $template;
function __construct($template){
$this->template = $template;
}
function parse(){
// use the template when parsing
}
}
class DefaultParsingHandler {
function parse(){
// just parses stuff
}
}
$myTemplatedParsingHandler = new TemplatedParsingHandler($myTemplate);
$myDefaultParsingHandler = new DefaultParsingHandler();
$myTemplatedParser = new Parser($myTemplatedParsingHandler);
$myDefaultParser = new Parser($myDefaultParsingHandler);
$myTemplatdResult = $myTemplatedParser->parse();
$myUntemplatedResult = $myDefaultParser->parse();
所以TemplatedParsingHandler知道它需要一个模板,但是解析器不需要知道它。它不属于它的业务:它不依赖于TemplatedParsingHandler如何进行解析,它需要知道的是它的ParsingHandler有一个parse()
方法。
可以使用接口来对ParseHandler强制执行最低parse()
要求,但PHP是一种动态语言,因此这有点与其预期的编码方法相反。在这里打鸭子很好。如果您初始化Parser的对象提供了parse()
方法,则不需要对此进行更明确的说明。
答案 1 :(得分:0)
基于此处评论的见解以及对工厂模式的其他研究,我找到了解决我问题的解决方案。它还提供了一个不使用静态调用的工厂方法。
class Parser
{
private $ParserProvider;
public function __construct( string $provider )
{
$this->ParserProvider = new \ReflectionClass($provider);
}
public function __invoke($data, $template = null)
{
$Parser = $this->ParserProvider->newInstance();
if(isset($template)) {
return $Parser->setTemplate($template)->parse($data);
} else {
return $Parser->parse($data);
}
}
}
创建一个没有模板的新解析器:
$ParserA = new Parser('ParserA');
$result = $ParserA($contents);
使用模板创建新的解析器:
$ParserB = new Parser('ParserB');
$result = $ParserB($contents, $template);