【问题标题】:PHP Forcing interface implementationPHP强制接口实现
【发布时间】:2013-02-19 23:31:32
【问题描述】:

我的应用程序中有一个 SystemInfoFactory 类,它有一个 getSystemInfo() 方法:

/**
 * Returns SystemInfo object based on which OS
 * server uses
 *
 * @return SystemInfoInterface SystemInfo object
 */
public function getSystemInfo()
{
    $os = $this->getOS();

    $systemInfo = null;

    switch ($os) {
        case "Linux":
            $systemInfo = new LinuxInfo();
            break;
        case "Darwin":
            $systemInfo = new OSXInfo();
            break;
        case "Windows":
            $systemInfo = new WindowsInfo();
            break;
    }
    return $systemInfo;
}

所以它会根据主机系统选择合适的对象。现在,每个“信息类”都实现了SystemInfo 接口(getArchitecture、getCPU 等方法),但是如您所见,我的代码中没有任何地方检查返回的对象是否真的实现了接口。在返回之前检查选定的 $systemInfo 对象是否实现它是否被认为是“好习惯”?这显然不是必需的,但如果有人扩展了这个应用程序(例如添加了 BSD 支持)并忘记实现所有方法,他可能更难以调试。

【问题讨论】:

  • 当有人使用接口但没有实现所有方法时,PHP不应该抛出错误吗??
  • 会的,但事实是,他可以只实现所有接口方法,而无需在他的类中显式定义implements SystemInfoInterface,代码将毫无问题地运行。
  • 目前,他可能只是忘记实现其中一种方法,应用程序会在某个时候抛出undefined method 错误,但真正的问题根源可能深埋在调用堆栈中。
  • 是的,这是 PHP 动态类型的问题。不知道如何解决。

标签: php interface


【解决方案1】:

这绝对是个好习惯。您在文档块中定义您的方法返回SystemInfo 的实例。您的来电者应该能够依赖它。这在您的代码中很简单:

/**
 * Returns SystemInfo object based on which OS
 * server uses
 *
 * @return SystemInfoInterface SystemInfo object
 */
public function getSystemInfo()
{
    $os = $this->getOS();

    $systemInfo = null;

    switch ($os) {
        case "Linux":
            $systemInfo = new LinuxInfo();
            break;
        case "Darwin":
            $systemInfo = new OSXInfo();
            break;
        case "Windows":
            $systemInfo = new WindowsInfo();
            break;
        default:
            throw new \RuntimeException('System not supported');
            break;
    }

    if (!$systeminfo instanceof SystemInfo) {
        throw new \RuntimeException('Invalid SystemInfo object returned');
    }

    return $systemInfo;
}

确保您声明您将从该方法调用中引发异常。这里的异常可以很清楚地说明发生了什么,而不必稍后在代码中追逐“未定义的方法”错误。

【讨论】:

    【解决方案2】:

    我想,这里的关键词是 Duck Typing。一个对象的接口是由它的方法和属性定义的,而不是它的祖先和实现的接口。

    看看这个:http://en.wikipedia.org/wiki/Duck_typing

    回到 PHP:如果你不检查对象是否实现了你的接口,它是完全有效的并且没有坏的风格。如果代码崩溃,则必须责怪该类的实现者。如果代码与if ($obj instanceof FancyInterface) { 混淆,我会觉得很烦人。

    【讨论】:

    • 您正在传播可怕的编码实践。该方法表明它将返回某种类型的对象。如果没有,那就是糟糕的代码。时期。故事结局。如果不是因为这些原因,你到底为什么会有接口?总是,总是,总是代码到接口。不执行。
    • 当然应该针对接口进行编码。但是,如果一个类应该实现一个接口,我希望它会这样做。我不想检查每个对象是否实现了某些方法或接口。我希望他们能做到他们承诺的事情。这是 Duck Typing 的好处之一。
    • 这种预期会浪费您大量的调试时间,而多行 3 行代码本来可以解决的。请注意,这甚至仅对基于工厂的方法是必要的,因为它们的返回值是可变的。否则,您可以简单地使用接口键入提示您的方法参数,并且只花费您几个字符。
    • 类型提示在适用的情况下非常棒。当然,在 docblock 中定义对象类型也是必须的。检查软件中心部分的接口,比如工厂,对我来说也是可以的。但是,见鬼,我不能同意在类、脚本等的所有可能部分检查接口。我是这样理解你的答案的,因为你没有像在 cmets 中那样提到它。我会只检查instanceof,其中第三方库是游戏的一部分 - 无论是我将对象交付给第 3 方还是接受来自第 3 方的对象。
    • 如果一个方法返回一个对象,并且它由开发人员控制,那么不检查它的接口实现是可以接受的。我的论点是,在这样的工厂方法中(在某些时候,可能会更改为更抽象的工厂方法),当 OP 说其他人可能会扩展并添加对其他平台的支持或添加新对象时,我认为这是一张便宜又合理的支票。
    猜你喜欢
    • 2015-05-08
    • 2015-10-23
    • 2021-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    • 1970-01-01
    相关资源
    最近更新 更多