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

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

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

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

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

7 个优秀 PHP OO设计 习惯包括:

  1. 保持谦虚。
  2. 做个好邻居。
  3. 避免看到美杜莎。
  4. 利用最弱的链接。
  5. 您是橡皮,我是胶水。
  6. 限制传播。
  7. 考虑使用设计模式。

一、保持谦虚

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

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

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

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

坏习惯:公开公共字段

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

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

<?php

  1. class Person
  2. {
  3. public $prefix;
  4. public $givenName;
  5. public $familyName;
  6. public $suffix;
  7. }
  8.  
  9. $person = new Person();
  10. $person->prefix = "Mr.";
  11. $person->givenName = "John";
  12.  
  13. echo($person->prefix);
  14. echo($person->givenName);
  15. ?>

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

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

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

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

  1. <?php
  2. class Person
  3. {
  4. private $prefix;
  5. private $givenName;
  6. private $familyName;
  7. private $suffix;
  8.  
  9. public function setPrefix($prefix) {
  10. $this->prefix = $prefix;
  11. }
  12.  
  13. public function getPrefix() {
  14. return $this->prefix;
  15. }
  16.  
  17. public function setGivenName($gn) {
  18. $this->givenName = $gn;
  19. }
  20.  
  21. public function getGivenName() {
  22. return $this->givenName;
  23. }
  24.  
  25. public function setFamilyName($fn) {
  26. $this->familyName = $fn;
  27. }
  28.  
  29. public function getFamilyName() {
  30. return $this->familyName;
  31. }
  32.  
  33. public function setSuffix($suffix) {
  34. $this->suffix = $suffix;
  35. }
  36.  
  37. public function getSuffix() {
  38. return $this->suffix;
  39. }
  40. }
  41.  
  42. $person = new Person();
  43. $person->setPrefix("Mr.");
  44. $person->setGivenName("John");
  45.  
  46. echo($person->getPrefix());
  47. echo($person->getGivenName());
  48. ?>

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

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

清单 3. 使用不同内部实现的另一个示例

  1. <?php
  2. class Person
  3. {
  4. private $personName = array();
  5.  
  6. public function setPrefix($prefix) {
  7. $this->personName['prefix'] = $prefix;
  8. }
  9.  
  10. public function getPrefix() {
  11. return $this->personName['prefix'];
  12. }
  13.  
  14. public function setGivenName($gn) {
  15. $this->personName['givenName'] = $gn;
  16. }
  17.  
  18. public function getGivenName() {
  19. return $this->personName['givenName'];
  20. }
  21.  
  22. /* etc... */
  23. }
  24.  
  25. /**
  26. * Even though the internal implementation changed, the code here stays exactly
  27. * the same. The change has been encapsulated only to the Person class.
  28. */
  29. $person = new Person();
  30. $person->setPrefix("Mr.");
  31. $person->setGivenName("John");
  32.  
  33. echo($person->getPrefix());
  34. echo($person->getGivenName());
  35.  
  36. ?>

二、做个好邻居

在构建类时,它应当正确地处理自己的错误。如果该类不知道如何处理错误,则应当以其调用者理解的格式封装这些错误。此外,避免返回空对象或者状态无 效的对象。许多时候,只需通过检验参数并抛出特定异常说明提供参数无效的原因就可以实现这一点。在您养成这个习惯时,它可以帮您 — 和维护代码或使用对象的人员 — 节省很多时间。

坏习惯:不处理错误

考虑清单 4 中所示的示例,该示例将接受一些参数并返回填充了一些值的 Person 对象。但是,在 parsePersonName() 方法中,没有验证提供的 $val 变量是否为空、是否是零长度字符串或者字符串是否使用无法解析的格式。parsePersonName() 方法不返回 Person 对象,但是返回 null。使用这种方法的管理员或程序员可能会觉得很麻烦 — 至少他们现在需要开始设置断点并调试 PHP 脚本。

清单 4. 不抛出或处理错误的坏习惯

  1. <?php
  2. class PersonUtils
  3. {
  4. public static function parsePersonName($format, $val) {
  5. if (strpos(",", $val) > 0) {
  6. $person = new Person();
  7. $parts = split(",", $val); // Assume the value is last, first
  8. $person->setGivenName($parts[1]);
  9. $person->setFamilyName($parts[0]);
  10. }
  11.  
  12. return $person;
  13. }
  14. }
  15. ?>

清单 4 中的 parsePersonName() 方法可以修改为在 if 条件外部初始化 Person 对象,确保总是获得有效的 Person 对象。但是,您得到的是没有 set 属性的 Person,这仍然没有很好地改善您的困境。

好习惯:每个模块都处理自己的错误

不要让调用方凭空猜测,而是对参数进行预先验证。如果未设置的变量无法生成有效的结果,请检查变量并抛出 InvalidArgumentException。如果字符串不能为空或者必须为特定格式,请检查格式并抛出异常。清单 5 解释了如何在演示一些基本验证的 parsePerson() 方法中创建异常以及一些新条件。

清单 5. 抛出错误的好习惯

  1. <?php
  2. class InvalidPersonNameFormatException extends LogicException {}
  3.  
  4. class PersonUtils
  5. {
  6. public static function parsePersonName($format, $val) {
  7. if (!$format) {
  8. throw new InvalidPersonNameFormatException("Invalid PersonName format.");
  9. }
  10.  
  11. if ((!isset($val)) || strlen($val) == 0) {
  12. throw new InvalidArgumentException("Must supply a non-null value to parse.");
  13. }
  14. }
  15. }
  16. ?>

最终目的是希望人们能够使用您的类,而不必了解其中的工作原理。如果他们使用的方法不正确或者不是按照期望的方法使用,也不需要猜测不能工作的原因。作为一个好邻居,您需要知道对您的类进行重用的人并没有特异功能,因此您需要解决猜测的问题。

三、避免看到美杜莎

在我最初了解 OO 概念时,我十分怀疑接口是否真正有帮助。我的同事给我打了个比方,说不使用接口就好像看到美杜莎的头。在希腊神话中,美杜莎是长着蛇发的女怪。凡是看了她 一眼的人都会变成石头。杀死美杜莎的珀尔休斯通过在盾上观察她的影子,避免了变成石头而得以与她对抗。

接口就是对付美杜莎的镜子。当您使用一个特定的具体实现时,代码也必须随着实现代码的更改而更改。直接使用实现将限制您的选择,因为您已经在本质上把类变成了 “石头”。

坏习惯:不使用接口

清单 6 显示了从数据库中装入 Person 对象的示例。它将获取人员的姓名并返回数据库中匹配的 Person 对象。

清单 6. 不使用接口的坏习惯

  1. <?php
  2. class DBPersonProvider
  3. {
  4. public function getPerson($givenName, $familyName) {
  5.  
  6. /** go to the database, get the person... */
  7. $person = new Person();
  8. $person->setPrefix("Mr.");
  9. $person->setGivenName("John");
  10. return $person;
  11. }
  12. }
  13.  
  14. /** I need to get person data... */
  15. $provider = new DBPersonProvider();
  16. $person = $provider->getPerson("John", "Doe");
  17.  
  18. echo($person->getPrefix());
  19. echo($person->getGivenName());
  20.  
  21. ?>

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

好习惯:使用接口

清单 7 显示了一个代码示例,在实现了加载用户的新方法后并没有进行更改。该示例显示了一个名为 PersonProvider 的接口,该接口将声明单个方法。如果任何代码使用 PersonProvider,代码都禁止直接使用实现类。相反,它就像是一个实际对象一样使用 PersonProvider

清单 7. 使用接口的好习惯

  1. <?php
  2. interface PersonProvider
  3. {
  4. public function getPerson($givenName, $familyName);
  5. }
  6.  
  7. class DBPersonProvider implements PersonProvider
  8. {
  9. public function getPerson($givenName, $familyName) {
  10.  
  11. /** pretend to go to the database, get the person... */
  12. $person = new Person();
  13. $person->setPrefix("Mr.");
  14. $person->setGivenName("John");
  15. return $person;
  16. }
  17. }
  18.  
  19. class PersonProviderFactory
  20. {
  21. public static function createProvider($type) {
  22. if ($type == 'database') {
  23. return new DBPersonProvider();
  24. } else {
  25. return new NullProvider();
  26. }
  27. }
  28. }
  29.  
  30. $config = 'database';
  31.  
  32. /** I need to get person data... */
  33. $provider = PersonProviderFactory::createProvider($config);
  34. $person = $provider->getPerson("John", "Doe");
  35.  
  36. echo($person->getPrefix());
  37. echo($person->getGivenName());
  38. ?>

在使用接口时,尝试避免直接引用实现类。相反,使用对象外部的内容可以提供正确的实现。如果您的类将装入基于某些逻辑的实现,它仍然需要获取所有实现类的定义,并且那样做也无法取得任何效果。

您可以使用 Factory 模式来创建实现接口的实现类的实例。根据约定,factory 方法将以 create 为开头并返回接口。它可以为您的 factory 获取必要的参数以计算出应当返回哪个实现类。

在清单 7 中,createProvider() 方法只是获取 $type。如果 $type 被设为 database,工厂将返回 DBPersonProvider 的实例。从数据库中装入人员的任何新实现都不要求在使用工厂和接口的类中进行任何更改。DBPersonProvider 将实现 PersonProvider 接口并且拥有 getPerson() 方法的实际实现。

四、利用最弱的链接

将模块松散耦合 在一起是件好事情;它是允许您封装更改的属性之一。另外两个习惯 — “保持谨慎” 和 “避免看到美杜莎” — 可帮助您构建松散耦合的模块。要实现松散耦合的类,可通过养成降低类依赖关系的习惯实现。

坏习惯:紧密耦合

在清单 8 中,降低依赖关系并不是必须降低使用对象的客户机的依赖关系。相反,该示例将演示如何降低与正确类的依赖关系并最小化这种依赖关系。

清单 8. Address 中紧密耦合的坏习惯

  1. <?php
  2. require_once "./AddressFormatters.php";
  3.  
  4. class Address
  5. {
  6. private $addressLine1;
  7. private $addressLine2;
  8. private $city;
  9. private $state; // or province...
  10. private $postalCode;
  11. private $country;
  12.  
  13. public function setAddressLine1($line1) {
  14. $this->addressLine1 = $line1;
  15. }
  16.  
  17. /** accessors, etc... */
  18.  
  19. public function getCountry() {
  20. return $this->country;
  21. }
  22.  
  23. public function format($type) {
  24. if ($type == "inline") {
  25. $formatter = new InlineAddressFormatter();
  26. } else if ($type == "multiline") {
  27. $formatter = new MultilineAddressFormatter();
  28. } else {
  29. $formatter = new NullAddressFormatter();
  30. }
  31.  
  32. return $formatter->format(
  33. $this->getAddressLine1(),
  34. $this->getAddressLine2(),
  35. $this->getCity(),
  36. $this->getState(),
  37. $this->getPostalCode(),
  38. $this->getCountry()
  39. );
  40. }
  41. }
  42.  
  43. $addr = new Address();
  44. $addr->setAddressLine1("123 Any St.");
  45. $addr->setAddressLine2("Ste 200");
  46. $addr->setCity("Anytown");
  47. $addr->setState("AY");
  48. $addr->setPostalCode("55555-0000");
  49. $addr->setCountry("US");
  50.  
  51. echo($addr->format("multiline"));
  52. echo("
  53. ");
  54.  
  55. echo($addr->format("inline"));
  56. echo("
  57. ");
  58. ?>

Address 对象上调用 format() 方法的代码可能看上去很棒 — 这段代码所做的是使用 Address 类,调用 format() 并完成。相反,Address 类就没那么幸运。它需要了解用于正确格式化的各种格式化方法,这可能使 Address 对象无法被其他人很好地重用,尤其是在其他人没有兴趣在 format() 方法中使用格式化方法类的情况下。虽然使用 Address 的代码没有许多依赖关系,但是 Address 类却有大量代码,而它可能只是一个简单的数据对象。

Address 类与知道如何格式化 Address 对象的实现类紧密耦合。

好习惯:在对象之间松散耦合

在构建优秀的 OO 设计时,必须考虑称为关注点分离(Separation of Concerns,SoC)的概念。SoC 指尝试通过真正关注的内容分离对象,从而降低耦合度。在最初的 Address 类中,它必须关注如何进行格式化。这可能不是优秀的设计。然而,Address 类应当考虑 Address 的各部分,而某种格式化方法应当关注如何正确格式化地址。

在清单 9 中,格式化地址的代码被移到接口、实现类和工厂中 — 养成 “使用接口” 的习惯。现在,AddressFormatUtils 类负责创建格式化方法并格式化 Address。任何其他对象现在都可以使用 Address 而不必担心要求获得格式化方法的定义。

清单 9. 在对象之间松散耦合的好习惯

  1. <?php
  2.  
  3. interface AddressFormatter
  4. {
  5. public function format($addressLine1, $addressLine2, $city, $state, $postalCode, $country);
  6. }
  7.  
  8. class MultiLineAddressFormatter implements AddressFormatter
  9. {
  10. public function format($addressLine1, $addressLine2, $city, $state, $postalCode, $country) {
  11. return sprintf("%s
  12. %s
  13. %s, %s %s
  14. %s", $addressLine1, $addressLine2, $city, $state, $postalCode, $country);
  15. }
  16. }
  17.  
  18. class InlineAddressFormatter implements AddressFormatter
  19. {
  20. public function format($addressLine1, $addressLine2, $city, $state, $postalCode, $country) {
  21. return sprintf("%s %s, %s, %s %s %s", $addressLine1, $addressLine2, $city, $state, $postalCode, $country);
  22. }
  23. }
  24.  
  25. class AddressFormatUtils
  26. {
  27. public static function formatAddress($type, $address) {
  28. $formatter = AddressFormatUtils::createAddressFormatter($type);
  29.  
  30. return $formatter->format(
  31. $address->getAddressLine1(),
  32. $address->getAddressLine2(),
  33. $address->getCity(), $address->getState(),
  34. $address->getPostalCode(),
  35. $address->getCountry()
  36. );
  37. }
  38.  
  39. private static function createAddressFormatter($type)
  40. {
  41. if ($type == "inline") {
  42. $formatter = new InlineAddressFormatter();
  43. } else if ($type == "multiline") {
  44. $formatter = new MultilineAddressFormatter();
  45. } else {
  46. $formatter = new NullAddressFormatter();
  47. }
  48.  
  49. return $formatter;
  50. }
  51. }
  52.  
  53. $addr = new Address();
  54. $addr->setAddressLine1("123 Any St.");
  55. $addr->setAddressLine2("Ste 200");
  56. $addr->setCity("Anytown");
  57. $addr->setState("AY");
  58. $addr->setPostalCode("55555-0000");
  59. $addr->setCountry("US");
  60.  
  61. echo(AddressFormatUtils::formatAddress("multiline", $addr));
  62. echo("
  63. ");
  64.  
  65. echo(AddressFormatUtils::formatAddress("inline", $addr));
  66. echo("
  67. ");
  68. ?>

当然,缺点是只要使用模式,通常就意味着工件(类、文件)的数量会增加。但是,通过减少每个类中的维护可以弥补这个缺点,甚至在获得正确的可重用性时反而可以减少工件量。

五、您是橡皮;我是胶水

具有高度内聚力的 OO 设计被集中并组织到相关模块中。了解 “关注点” 对于决定如何紧密地联系函数和类十分重要。

坏习惯:降低内聚力

当设计的内聚力较低 时,它就不能良好地组织类和方法。意大利面条式代码(spaghetti code)一词通常用于描述捆绑在一起并且具有低内聚力的类和方法。清单 10 提供了意大利面条式代码的示例。相对通用的 Utils 类将使用许多不同对象并且有许多依赖关系。它执行很多操作,因而很难实现重用。

清单 10. 降低内聚力的坏习惯

  1. <?php
  2. class Utils
  3. {
  4. public static function formatAddress($formatType, $address1, $address2, $city, $state) {
  5. return "some address string";
  6. }
  7.  
  8. public static function formatPersonName($formatType, $givenName, $familyName) {
  9. return "some person name";
  10. }
  11.  
  12. public static function parseAddress($formatType, $val) {
  13. // real implementation would set values, etc...
  14. return new Address();
  15. }
  16.  
  17. public static function parseTelephoneNumber($formatType, $val) {
  18. // real implementation would set values, etc...
  19. return new TelephoneNumber();
  20. }
  21. }
  22. ?>

好习惯:利用高内聚力

高内聚力 指将相互关联的类和方法分组在一起。如果方法和类都具有高度的内聚力,则可以轻松地分解整个组而不影响设计。具有高内聚力的设计将提供降低耦合的机会。清单 11 显示了被较好组织到类中的两个方法。AddressUtils 类将包含用于处理 Address 类的方法,显示了与地址相关的方法之间的高度内聚力。同样地,PersonUtils 将包含专门处理 Person 对象的方法。这两个拥有高度内聚力方法的新类的耦合性都很低,因为可以完全独立地使用。

清单 11. 高内聚力的好习惯

  1. <?php
  2. class AddressUtils
  3. {
  4. public static function formatAddress($formatType, $address1, $address2, $city, $state) {
  5. return "some address string";
  6. }
  7.  
  8. public static function parseAddress($formatType, $val) {
  9. // real implementation would set values, etc...
  10. return new Address();
  11. }
  12. }
  13.  
  14. class PersonUtils
  15. {
  16. public static function formatPersonName($formatType, $givenName, $familyName) {
  17. return "some person name";
  18. }
  19.  
  20. public static function parsePersonName($formatType, $val) {
  21. // real implementation would set values, etc...
  22. return new PersonName();
  23. }
  24. }
  25. ?>

六、限制传播

我经常对我所在的软件团队(我在其中担任技术主管或架构师)的成员提起,OO 语言最大的敌人是复制和粘贴操作。当在缺少预先 OO 设计的情况下使用时,没有任何操作会像在类之间复制代码那样具有破坏性。无论何时,如果想将代码从一个类复制到下一个类中,请停下来并考虑如何使用类层次 结构利用类似功能或相同功能。在大多数情况下,使用优秀设计后,您将会发现完全没有必要复制代码。

坏习惯:不使用类层次结构

清单 12 显示了部分类的简单示例。它们从重复的字段和方法开始 — 从长远来看,不利于应用程序作出更改。如果 Person 类中有缺陷,则 Employee 类中也很可能有一个缺陷,因为看上去似乎实现是在两个类之间复制的。

清单 12. 不使用层次结构的坏习惯

  1. <?php
  2. class Person
  3. {
  4. private $givenName;
  5. private $familyName;
  6. }
  7.  
  8. class Employee
  9. {
  10. private $givenName;
  11. private $familyName;
  12. }
  13. ?>

继承 是一个很难入手的习惯,因为构建正确继承模型的分析通常需要花费大量时间。反过来,使用 Ctrl+C 组合键和 Ctrl+V 组合键构建新实现只需几秒钟。但是省下的这部分时间通常会在维护阶段迅速抵销掉,因为应用程序实际上将花费大量进行维护。

好习惯:利用继承

在清单 13 中,新 Employee 类将扩展 Person 类。它现在将继承所有通用方法并且不重新实现这些方法。此外,清单 13 显示了抽象方法的用法,演示如何将基本功能放入基类中以及如何阻止实现类使用特定函数。

清单 13. 利用继承的好习惯

  1. <?php
  2. abstract class Person
  3. {
  4. private $givenName;
  5. private $familyName;
  6.  
  7. public function setGivenName($gn) {
  8. $this->givenName = $gn;
  9. }
  10.  
  11. public function getGivenName() {
  12. return $this->givenName;
  13. }
  14.  
  15. public function setFamilyName($fn) {
  16. $this->familyName = $fn;
  17. }
  18.  
  19. public function getFamilyName() {
  20. return $this->familyName;
  21. }
  22.  
  23. public function sayHello() {
  24. echo("Hello, I am ");
  25. $this->introduceSelf();
  26. }
  27.  
  28. abstract public function introduceSelf();
  29.  
  30. }
  31.  
  32. class Employee extends Person
  33. {
  34. private $role;
  35.  
  36. public function setRole($r) {
  37. $this->role = $r;
  38. }
  39.  
  40. public function getRole() {
  41. return $this->role;
  42. }
  43.  
  44. public function introduceSelf() {
  45. echo($this->getRole() . " " . $this->getGivenName() . " " . $this->getFamilyName());
  46. }
  47. }
  48. ?>

七、考虑使用设计模式

设计模式指对象和方法的常见交互,并且时间证明它可以解决某些问题。当您考虑使用设计模式时,您就需要了解类之间如何进行交互。它是构建类及其交互操作的简单方法,无需重蹈他人的覆辙,并从经过证明的设计中获益。

坏习惯:一次考虑一个对象

实际上没有适当的代码示例可以演示如何考虑使用模式(尽管有丰富的优秀示例可以显示模式实现)。但是,一般而言,您知道在满足以下条件时一次只能考虑一个对象:

  • 不会提前设计对象模型。
  • 开始编写单一方法的实现,而无需去掉大部分模型。
  • 在交谈中不使用设计模式名而宁愿谈论实现。

好习惯:同时添加模式中形成的对象

一般而言,当您在执行以下操作时就是在考虑使用模式:

  • 提前构建类及其交互操作。
  • 根据模式套用类。
  • 使用模式名,如 Factory、SingletonFacade
  • 去掉大部分模型,然后开始添加实现。

结束语

在 PHP 中养成良好的 OO 习惯将帮助您构建更稳定、更易于维护和更易于扩展的应用程序。记住:

  • 保持谨慎。
  • 做个好邻居。
  • 避免看到美杜莎。
  • 利用最弱的链接。
  • 您是橡皮,我是胶水。
  • 限制传播。
  • 考虑使用模式。

当您养成并应用这些习惯后,您很可能会惊讶地发现应用程序在质量上的飞跃。

参考资料

学习

摘自:http://www.ibm.com/developerworks/cn/opensource/os-php-7oohabits/?ca=drs-tp4408

PHP程序员,因该养成 7 个面向对象的好习惯的更多相关文章

  1. Java程序员应该了解的10个面向对象设计原则

    面向对象设计原则: 是OOPS(Object-Oriented Programming System,面向对象的程序设计系统)编程的核心,但大多数Java程序员追逐像Singleton.Decorat ...

  2. Java程序员应该知道的10个面向对象理论

    英文原文:10-object-oriented-design-principles 面向对象理论是面向对象编程的核心,但是我发现大部分 Java 程序员热衷于像单例模式.装饰者模式或观察者模式这样的设 ...

  3. (转)Java程序员应该了解的10个面向对象设计原则

    面向对象设计原则是OOPS(Object-Oriented Programming System,面向对象的程序设计系统)编程的核心,但大多数Java程序员追逐像Singleton.Decorator ...

  4. Java程序员应当知道的10个面向对象设计原则

    面向对象设计原则是OOPS编程的核心, 但我见过的大多数Java程序员热心于像Singleton (单例) . Decorator(装饰器).Observer(观察者) 等设计模式,而没有把足够多的注 ...

  5. [译]Java 程序员应该了解的 10 个面向对象设计原则

    面向对象设计原则是OOPS(Object-Oriented Programming System,面向对象的程序设计系统)编程的核心,但大多数Java程序员追逐像Singleton.Decorator ...

  6. 身为java程序员你需要知道的网站(包含书籍,面试题,架构...)

    推荐几本书<高级java程序员值得拥有的10本书>,     首页 所有文章 资讯 Web 架构 基础技术 书籍 教程 我要投稿 更多频道 » - 导航条 - 首页 所有文章 资讯 Web ...

  7. PHP程序员求职经验总结

    这次来深圳找工作,是我人生中第一次正式的求职,也是第一份正式的工作.这几天收获不少,总结一下,"供后人参考"; 从7月23来深圳到今天刚好一个星期,这7天内我发了18封求职邮件,在 ...

  8. Python学习手册(第4版) - 专业程序员的养成完整版PDF免费下载_百度云盘

    Python学习手册(第4版) - 专业程序员的养成完整版PDF免费下载_百度云盘 提取码:g7v1 作者简介 作为全球Python培训界的领军人物,<Python学习手册:第4版>作者M ...

  9. Python编程快速上手 让繁琐工作自动化 - 专业程序员的养成完整版PDF免费下载_百度云盘

    Python编程快速上手  让繁琐工作自动化 - 专业程序员的养成完整版PDF免费下载_百度云盘 提取码:7qm4 有关本书 累计销售超过10万册 零基础编程者的不二之选 基于Python3编写 通用 ...

随机推荐

  1. 什么是co-training

    首先先认识下什么是co-training: 在计算机视觉中,我们都知道训练一个分类器的时候,我们需要两类样本,分别是正样本和负样本.监督训练又可以成为off-line training,就是提前准备好 ...

  2. 数据库防火墙如何防范SQL注入行为

    SQL注入是当前针对数据库安全进行外部攻击的一种常见手段.现有主流应用大多基于B/S架构开发,SQL注入的攻击方式正是利用web层和通讯层的缺陷对数据库进行外部恶意攻击.将SQL命令巧妙的插入通讯的交 ...

  3. 面试官的七种武器:Java篇

    起源 自己经历过的面试也不少了,互联网的.外企的,都有.总结一下这些面试的经验,发现面试官问的问题其实不外乎几个大类,玩不出太多新鲜玩意的.细细想来,面试官拥有以下七种武器.恰似古龙先生笔下的武侠世界 ...

  4. 【NDK开发】使用NDK开发android

    今天学习了一下android NDK,所以记录下来.据说NDK从r7开始自带编译器,在windows上无需配置cygwin的环境.现在我使用NDK r10来开发. 上午搭建的NDK并写了一个实例,不过 ...

  5. 从日常开发说起,浅谈HTTP协议是做什么的。

    引言 HTTP协议作为Web开发的基础一直被大多数人所熟知,不过相信有很多人只知其一不知其二.比如咱们经常用到的session会话机制是如何实现的,可能很多人都说不出来吧.其实session会话就是H ...

  6. 由DataGridTextColumn不能获取到父级DataContext引发的思考

    在项目中使用DataGrid需要根据业务动态隐藏某些列,思路都是给DataGrid中列的Visibility属性绑定值来实现(项目使用MVVM),如下 <DataGridTextColumn H ...

  7. Java Web技术总结(目录)

    来源于:http://www.jianshu.com/p/539bdb7d6cfa Java Web技术经验总结(一) Java Web技术经验总结(二) Java Web技术经验总结(三) Java ...

  8. OC基础--ARC的基本使用

    一.ARC的判断准则:只要没有强指针指向对象,就会释放对象 二.ARC特点: 1>不允许使用release.retain.retainCount 2>允许重写dealloc,但是不允许调用 ...

  9. Oracle11g 32位安装步骤

    1.注意Oracle分成两个文件,下载完后,将两个文件解压到同一目录下即可. 路径名称中,最好不要出现中文,也不要出现空格等不规则字符.官方下地址:http://www.oracle.com/tech ...

  10. Bzoj2440 完全平方数

    Time Limit: 10000MS   Memory Limit: 131072KB   64bit IO Format: %lld & %llu Description 小 X 自幼就很 ...