<?php
class Person
{
private $personName = array();
public function setPrefix($prefix)
{
$this->personName['prefix'] = $prefix;
}
public function getPrefix()
{
return $this->personName['prefix'];
}
public function setGivenName($gn)
{
$this->personName['givenName'] = $gn;
}
public function getGivenName()
{
return $this->personName['givenName'];
}
/* etc... */
}
/*
* Even though the internal implementation changed, the code here stays exactly
* the same. The change has been encapsulated only to the Person class.
*/
$person = new Person();
$person->setPrefix("Mr.");
$person->setGivenName("John");
echo($person->getPrefix());
echo($person->getGivenName());
?>
做个好邻居
在构建类时,它应当正确地处理自己的错误。如果该类不知道如何处理错误,则应当以其调用者理解的格式封装这些错误。此外,避免返回空对象或者状态无效的对象。许多时候,只需通过检验参数并抛出特定异常说明提供参数无效的原因就可以实现这一点。在您养成这个习惯时,它可以帮您 — 和维护代码或使用对象的人员 — 节省很多时间。
坏习惯:不处理错误
考虑清单 4 中所示的示例,该示例将接受一些参数并返回填充了一些值的 Person 对象。但是,在 parsePersonName() 方法中,没有验证提供的 $val 变量是否为空、是否是零长度字符串或者字符串是否使用无法解析的格式。parsePersonName() 方法不返回 Person 对象,但是返回 null。使用这种方法的管理员或程序员可能会觉得很麻烦 — 至少他们现在需要开始设置断点并调试 PHP 脚本。
清单 4. 不抛出或处理错误的坏习惯
复制代码 代码如下:
class PersonUtils
{
public static function parsePersonName($format, $val)
{
if (strpos(",", $val) > 0) {
$person = new Person();
$parts = split(",", $val); // Assume the value is last, first
$person->setGivenName($parts[1]);
$person->setFamilyName($parts[0]);
}
return $person;
}
}
清单 4 中的 parsePersonName() 方法可以修改为在 if 条件外部初始化 Person 对象,确保总是获得有效的 Person 对象。但是,您得到的是没有 set 属性的 Person,这仍然没有很好地改善您的困境。
好习惯:每个模块都处理自己的错误
不要让调用方凭空猜测,而是对参数进行预先验证。如果未设置的变量无法生成有效的结果,请检查变量并抛出 InvalidArgumentException。如果字符串不能为空或者必须为特定格式,请检查格式并抛出异常。清单 5 解释了如何在演示一些基本验证的 parsePerson() 方法中创建异常以及一些新条件。
清单 5. 抛出错误的好习惯
复制代码 代码如下:
<?php
class InvalidPersonNameFormatException extends LogicException {}
class PersonUtils
{
public static function parsePersonName($format, $val)
{
if (! $format) {
throw new InvalidPersonNameFormatException("Invalid PersonName format.");
}
if ((! isset($val)) || strlen($val) == 0) {
throw new InvalidArgumentException("Must supply a non-null value to parse.");
}
}
}
?>
最终目的是希望人们能够使用您的类,而不必了解其中的工作原理。如果他们使用的方法不正确或者不是按照期望的方法使用,也不需要猜测不能工作的原因。作为一个好邻居,您需要知道对您的类进行重用的人并没有特异功能,因此您需要解决猜测的问题。
避免看到美杜莎
在我最初了解 OO 概念时,我十分怀疑接口是否真正有帮助。我的同事给我打了个比方,说不使用接口就好像看到美杜莎的头。在希腊神话中,美杜莎是长着蛇发的女怪。凡是看了她一眼的人都会变成石头。杀死美杜莎的珀尔休斯通过在盾上观察她的影子,避免了变成石头而得以与她对抗。
接口就是对付美杜莎的镜子。当您使用一个特定的具体实现时,代码也必须随着实现代码的更改而更改。直接使用实现将限制您的选择,因为您已经在本质上把类变成了 “石头”。
坏习惯:不使用接口
清单 6 显示了从数据库中装入 Person 对象的示例。它将获取人员的姓名并返回数据库中匹配的 Person 对象。
清单 6. 不使用接口的坏习惯
复制代码 代码如下: