如何使用(父)类作为其他(子)类的网关/接口?

时间:2016-04-29 23:10:29

标签: php oop php-7

我们假设您有一个名为' 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()方法,因为它随后成为了父类的方法。

有没有办法实现这一点,我忽略了?

2 个答案:

答案 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);