利用最弱的链接
将模块松散耦合 在一起是件好事情;它是允许您封装更改的属性之一。另外两个习惯 — “保持谨慎” 和 “避免看到美杜莎” — 可帮助您构建松散耦合的模块。要实现松散耦合的类,可通过养成降低类依赖关系的习惯实现。
坏习惯:紧密耦合
在清单 8 中,降低依赖关系并不是必须降低使用对象的客户机的依赖关系。相反,该示例将演示如何降低与正确类的依赖关系并最小化这种依赖关系。
◆清单 8. Address 中紧密耦合的坏习惯
| <?phprequire_once "./AddressFormatters.php";class Address{ private $addressLine1; private $addressLine2;private $city; private $state; // or province... private $postalCode; private $country; public function setAddressLine1($line1) {$this->addressLine1 = $line1; }/* accessors, etc... */ public function getCountry(){ return $this->country; }public function format($type) {if ($type == "inline") {$formatter = new InlineAddressFormatter(); } else if ($type == "multiline") { $formatter = new MultilineAddressFormatter();} else { $formatter = new NullAddressFormatter(); } return $formatter->format($this->getAddressLine1(), $this->getAddressLine2(), $this->getCity(), $this->getState(), $this->getPostalCode(), $this->getCountry()); }}$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($addr->format("multiline"));echo("n");echo($addr->format("inline"));echo("n");?> | 在 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. 在对象之间松散耦合的好习惯
| <?phpinterface 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");?> | 当然,缺点是只要使用模式,通常就意味着工件(类、文件)的数量会增加。但是,通过减少每个类中的维护可以弥补这个缺点,甚至在获得正确的可重用性时反而可以减少工件量。 |