我的应用程序中有一个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;
}
因此根据主机系统选择合适的对象。现在,每个"info类"都实现了SystemInfo
接口(getArchitecture、getCPU等方法),但是正如你所看到的,在我的代码中没有任何地方检查返回的对象是否真正实现了接口。它会被认为是"良好的做法"检查是否选定的$systemInfo
对象实现它之前返回?这显然不是必需的,但是如果有人扩展了这个应用程序(例如添加了BSD支持),并且忘记实现所有的方法,那么对他来说调试可能会更加困难。
这绝对是个好习惯。您在docblock中定义您的方法返回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;
}
确保声明将从该方法调用抛出异常。这里的异常使发生的事情变得非常清楚,而不必在后面的代码中追踪"未定义的方法"错误。
我想,这里的关键字是Duck Typing。对象的接口由其方法和属性定义,而不是由其祖先和实现的接口定义。
看看这个:http://en.wikipedia.org/wiki/Duck_typing
回到PHP:如果你不检查对象是否实现了你的接口,它是完全有效的,没有坏的风格。如果代码崩溃,则该类的实现者必须受到指责。如果代码与if ($obj instanceof FancyInterface) {
混淆,我会觉得很烦人。