清單 9. 在對象之間鬆散耦合的好習慣

<?php

interface AddressFormatter
{
public function format($addressLine1, $addressLine2, $city, $state,
$postalCode, $country);
}

class MultiLineAddressFormatter implements AddressFormatter
{
public function format($addressLine1, $addressLine2, $city, $state,
$postalCode, $country)
{
return sprintf("%sn%sn%s, %s %sn%s",
$addressLine1, $addressLine2, $city, $state, $postalCode, $country);
}
}

class InlineAddressFormatter implements AddressFormatter
{
public function format($addressLine1, $addressLine2, $city, $state,
$postalCode, $country)
{
return sprintf("%s %s, %s, %s %s %s",
$addressLine1, $addressLine2, $city, $state, $postalCode, $country);
}
}

class AddressFormatUtils
{
public static function formatAddress($type, $address)
{
$formatter = AddressFormatUtils::createAddressFormatter($type);

return $formatter->format($address->getAddressLine1(),
$address->getAddressLine2(),
$address->getCity(), $address->getState(),
$address->getPostalCode(),
$address->getCountry());
}

private static function createAddressFormatter($type)
{
if ($type == "inline") {
$formatter = new InlineAddressFormatter();
} else if ($type == "multiline") {
$formatter = new MultilineAddressFormatter();
} else {
$formatter = new NullAddressFormatter();
}
return $formatter;
}
}

$addr = new Address();
$addr->setAddressLine1("123 Any St.");
$addr->setAddressLine2("Ste 200");
$addr->setCity("Anytown");
$addr->setState("AY");
$addr->setPostalCode("55555-0000");
$addr->setCountry("US");

echo(AddressFormatUtils::formatAddress("multiline", $addr));
echo("n");

echo(AddressFormatUtils::formatAddress("inline", $addr));
echo("n");
?>


當然,缺點是只要使用模式,通常就意味著工件(類、文件)的數量會增加。但是,通過減少每個類中的維護可以彌補這個缺點,甚至在獲得正確的可重用性時反而可以減少工件量。







您是橡皮;我是膠水

具有高度內聚力的 OO 設計被集中並組織到相關模塊中。瞭解 「關注點」 對於決定如何緊密地聯繫函數和類十分重要。

壞習慣:降低內聚力

當設計的內聚力較低 時,它就不能良好地組織類和方法。意大利麵條式代碼(spaghetti code)一詞通常用於描述捆綁在一起並且具有低內聚力的類和方法。清單 10 提供了意大利麵條式代碼的示例。相對通用的 Utils 類將使用許多不同對象並且有許多依賴關係。它執行很多操作,因而很難實現重用。


清單 10. 降低內聚力的壞習慣

<?php

class Utils
{
public static function formatAddress($formatType, $address1,
$address2, $city, $state)
{
return "some address string";
}

public static function formatPersonName($formatType, $givenName,
$familyName)
{
return "some person name";
}

public static function parseAddress($formatType, $val)
{
// real implementation would set values, etc...
return new Address();
}

public static function parseTelephoneNumber($formatType, $val)
{
// real implementation would set values, etc...
return new TelephoneNumber();
}
}

?>


好習慣:利用高內聚力

高內聚力 指將相互關聯的類和方法分組在一起。如果方法和類都具有高度的內聚力,則可以輕鬆地分解整個組而不影響設計。具有高內聚力的設計將提供降低耦合的機會。清單 11 顯示了被較好組織到類中的兩個方法。AddressUtils 類將包含用於處理 Address 類的方法,顯示了與地址相關的方法之間的高度內聚力。同樣地,PersonUtils 將包含專門處理 Person 對象的方法。這兩個擁有高度內聚力方法的新類的耦合性都很低,因為可以完全獨立地使用。


清單 11. 高內聚力的好習慣

<?php

class AddressUtils
{
public static function formatAddress($formatType, $address1,
$address2, $city, $state)
{
return "some address string";
}

public static function parseAddress($formatType, $val)
{
// real implementation would set values, etc...
return new Address();
}

}

class PersonUtils
{
public static function formatPersonName($formatType, $givenName,
$familyName)
{
return "some person name";
}

public static function parsePersonName($formatType, $val)
{
// real implementation would set values, etc...
return new PersonName();
}
}

?>








限制傳播

我經常對我所在的軟件團隊(我在其中擔任技術主管或架構師)的成員提起,OO 語言最大的敵人是複製和粘貼操作。當在缺少預先 OO 設計的情況下使用時,沒有任何操作會像在類之間複製代碼那樣具有破壞性。無論何時,如果想將代碼從一個類複製到下一個類中,請停下來並考慮如何使用類層次 結構利用類似功能或相同功能。在大多數情況下,使用優秀設計後,您將會發現完全沒有必要複製代碼。

壞習慣:不使用類層次結構

清單 12 顯示了部分類的簡單示例。它們從重複的字段和方法開始 — 從長遠來看,不利於應用程序作出更改。如果 Person 類中有缺陷,則 Employee 類中也很可能有一個缺陷,因為看上去似乎實現是在兩個類之間複製的。


清單 12. 不使用層次結構的壞習慣

<?php
class Person
{
private $givenName;
private $familyName;
}

class Employee
{
private $givenName;
private $familyName;
}

?>


繼承 是一個很難入手的習慣,因為構建正確繼承模型的分析通常需要花費大量時間。反過來,使用 Ctrl+C 組合鍵和 Ctrl+V 組合鍵構建新實現只需幾秒鐘。但是省下的這部分時間通常會在維護階段迅速抵銷掉,因為應用程序實際上將花費大量進行維護。

好習慣:利用繼承

在清單 13 中,新 Employee 類將擴展 Person 類。它現在將繼承所有通用方法並且不重新實現這些方法。此外,清單 13 顯示了抽象方法的用法,演示如何將基本功能放入基類中以及如何阻止實現類使用特定函數。


清單 13. 利用繼承的好習慣

<?php
abstract class Person
{
private $givenName;
private $familyName;

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 sayHello()
{
echo("Hello, I am ");
$this->introduceSelf();
}

abstract public function introduceSelf();

}

class Employee extends Person
{
private $role;

public function setRole($r)
{
$this->role = $r;
}

public function getRole()
{
return $this->role;
}

public function introduceSelf()
{
echo($this->getRole() . " " . $this->getGivenName() . " " .
$this->getFamilyName());
}
}
?>








考慮使用模式

設計模式指對象和方法的常見交互,並且時間證明它可以解決某些問題。當您考慮使用設計模式時,您就需要瞭解類之間如何進行交互。它是構建類及其交互操作的簡單方法,無需重蹈他人的覆轍,並從經過證明的設計中獲益。

壞習慣:一次考慮一個對象

實際上沒有適當的代碼示例可以演示如何考慮使用模式(儘管有豐富的優秀示例可以顯示模式實現)。但是,一般而言,您知道在滿足以下條件時一次只能考慮一個對象:

  • 不會提前設計對象模型。
  • 開始編寫單一方法的實現,而無需去掉大部分模型。
  • 在交談中不使用設計模式名而寧願談論實現。

好習慣:同時添加模式中形成的對象

一般而言,當您在執行以下操作時就是在考慮使用模式:

  • 提前構建類及其交互操作。
  • 根據模式套用類。
  • 使用模式名,如 Factory、SingletonFacade
  • 去掉大部分模型,然後開始添加實現。







結束語

在 PHP 中養成良好的 OO 習慣將幫助您構建更穩定、更易於維護和更易於擴展的應用程序。記住:

  • 保持謹慎。
  • 做個好鄰居。
  • 避免看到美杜莎。
  • 利用最弱的鏈接。
  • 您是橡皮,我是膠水。
  • 限制傳播。
  • 考慮使用模式。

當您養成並應用這些習慣後,您很可能會驚訝地發現應用程序在質量上的飛躍。



參考資料

學習

創作者介紹

wEb 亂講

easonyo 發表在 痞客邦 PIXNET 留言(0) 人氣()