4000336031 微信 li1377174255

贵阳PHP培训学校

在PHP中养成7个面向对象的好习惯

来源:贵阳PHP培训学校 发布时间:2017/1/15 15:09:24

在 PHP 编程早期,PHP 代码在本质上是限于面向过程的。过程代码 的特征在于使用过程构建应用程序块。过程通过允许过程之间的调用提供某种程度的重用。

但是,没有面向对象的语言构造,程序员仍然可以把 OO 特性引入到 PHP 代码中。这样做有点困难并且会使代码难于阅读,因为它是混合范例(含有伪 OO 设计的过程语言)。使用 PHP 代码中的 OO 构造 — 例如能够定义和使用类、能够构建使用继承的类之间的关系以及能够定义接口 — 可以更轻松地构建符合 OO 实践的代码。

虽然没有过多模块化的纯过程设计运行得很好,但是 OO 设计的优点表现在维护上。由于典型应用程序的大部分生命周期都花费在维护上,因此代码维护是应用程序生命周期的重要部分。并且在开发过程中代码维护很容易被遗忘。如果在应用程序开发和部署方面存在竞争,那么长期可维护性可能被放在比较次要的地位。

模块化— OO 设计的主要特性之一 — 可以帮助完成这样的维护。模块化将帮助封装更改,这样可以随着时间的推移更轻松地扩展和修改应用程序。

总的来说,虽然构建 OO 软件的习惯不止 7 个,但是遵循这里的 7 个习惯可以使代码符合基本 OO 设计标准。它们将为您提供更牢固的基础,在此基础之上建立更多 OO 习惯并构建可轻松维护与扩展的软件。这些习惯针对模块化的几个主要特性。

7 个 PHP OO 习惯包括:



保持谦虚。


做个好邻居。


避免看到美杜莎。


利用较弱的链接。


您是橡皮;我是胶水。


限制传播。


考虑使用模式。

保持谦虚

保持谦虚指避免在类实现和函数实现中暴露自己。隐藏您的信息是一项基本习惯。如果不能养成隐藏实现细节的习惯,那么将很难养成任何其他习惯。信息隐藏也称为封装。

直接公开公共字段是一个坏习惯的原因有很多,较重要的原因是让您在实现更改中没有应有的选择。使用 OO 概念隔离更改,而封装在确保所作更改在本质上不是病毒性(viral)更改方面扮演不可或缺的角色。病毒性 更改是开始时很小的更改 — 如将保存三个元素的数组更改为一个只包含两个元素的数组。突然,您发现需要更改越来越多的代码以适应本应十分微不足道的更改。

开始隐藏信息的一种简单方法是保持字段私有并且用公共访问方法公开这些字段,就像家中的窗户一样。并没有让整面墙都朝外部开放,而只打开一两扇窗户。

除了允许您的实现隐藏在更改之后外,使用公共访问方法而非直接公开字段将允许您在基本实现的基础上进行构建,方法为覆盖访问方法的实现以执行略微不同于父方法的行为。它还允许您构建一个抽象实现,从而使实际实现委托给覆盖基本实现的类。

坏习惯:公开公共字段

在清单 1 的坏代码示例中,Person 对象的字段被直接公开为公共字段而非使用访问方法。虽然此行为十分诱人,尤其对于轻量级数据对象来说更是如此,但是它将对您提出限制。

清单 1. 公开公共字段的坏习惯<?php

class Person

{

   public $prefix;

   public $givenName;

   public $familyName;

   public $suffix;

}

$person = new Person();

$person->prefix = "Mr.";

$person->givenName = "John";

echo($person->prefix);

echo($person->givenName);

?>

如果对象有任何更改,则使用该对象的所有代码也都需要更改。例如,如果某人的教名、姓氏和其他名字被封装到 PersonName 对象中,则需要修改所有代码以适应更改。

好习惯:使用公共访问方法

通过使用的 OO 习惯(参见清单 2),同一个对象现在拥有私有字段而非公共字段,并且通过称为访问方法 的 get 和 set 公共方法谨慎地向外界公开私有字段。这些访问方法现在提供了一种从 PHP 类中获取信息的公共方法,这样在实现发生更改时,更改使用类的所有代码的需求很可能变小。

清单 2. 使用公共访问方法的好习惯<?php

class Person

{

   private $prefix;

   private $givenName;

   private $familyName;

   private $suffix;

   public function setPrefix($prefix)

   {

       $this->prefix = $prefix;

   }

   public function getPrefix()

   {

       return $this->prefix;

   }

   public function setGivenName($gn)

   {

       $this->givenName = $gn;

   }

   public function getGivenName()

   {

       return $this->givenName;

   }

   public function setFamilyName($fn)

   {

       $this->familyName = $fn;

   }

   public function getFamilyName()

   {

       return $this->familyName;

   }

   public function setSuffix($suffix)

   {

       $this->suffix = $suffix;

   }

   public function getSuffix()

   {

       return $suffix;

   }

}

$person = new Person();

$person->setPrefix("Mr.");

$person->setGivenName("John");

echo($person->getPrefix());

echo($person->getGivenName());

?>

乍看之下,这段代码可能会完成大量工作,并且实际上可能更多是在前端的工作。但是,通常,使用的 OO 习惯从长远来看十分划算,因为将极大地巩固未来更改。

在清单 3 中所示的代码版本中,我已经更改了内部实现以使用名称部件的关联数组。比较理想的情况是,我希望拥有错误处理并且更仔细地检查元素是否存在,但是本例的目的在于展示使用我的类的代码无需更改的程度 — 代码并没有察觉到类发生更改。记住采用 OO 习惯的原因是要谨慎封装更改,这样代码将更具有可扩展性并且更容易维护。

清单 3. 使用不同内部实现的另一个示例<?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. 不使用接口的坏习惯<?php

class DBPersonProvider

{

   public function getPerson($givenName, $familyName)

   {

       /* go to the database, get the person... */

       $person = new Person();

       $person->setPrefix("Mr.");

       $person->setGivenName("John");

       return $person;

   }

}

/* I need to get person data... */

$provider = new DBPersonProvider();

$person = $provider->getPerson("John", "Doe");

echo($person->getPrefix());

echo($person->getGivenName());

?>

在环境发生更改之前,从数据库中装入 Person 的代码都可以正常运行。例如,从数据库装入 Person 可能适用于个版本的应用程序,但是对于第二个版本,可能需要添加从 Web 服务装入人员的功能。其实,该类已经变成 “石头”,因为它在直接使用实现类并且现在能做的更改十分有限。

领取试听课
每天限量名额,先到先得
温馨提示:为不影响您的学业,来校区前请先电话或QQ咨询,方便我校安排相关的专业老师为您解答
  • 详情请进入 贵阳PHP培训学校

关于我们 | 招生信息 | 新闻中心 | 学校动态

版权所有:搜学搜课(www.soxsok.com)