10.3 外观模式
外观模式可以为复杂系统创建一个简单、清晰的接口。
10.3.1 问题
在系统中总会逐渐形成大量仅在系统自身内部有用的代码。系统也应该想类一样,提供定义清晰的公共接口,并对外隐藏内部结构。
在创建一个系统时,将不同的部分分层式一个很好的做法。例如,把系统分为逻辑层、数据库交互层和表现层等。应该尽可能地使这些分层相互独立,以便项目中某一部分的修改尽量不影响到其他地方。如果某层中的代码于另一层的代码存在耦合的话,修改就会变得很麻烦。
下面是一个混淆的程序代码,其功能是从文件中获取 log 信息并将它转成对象数据:
function getProductFileLines($file) {
return file($file);
}
function getProductObjectFromId($id, $productname) {
// 一些数据库查询
return new Product($id, $productname);
}
function getNameFromLine($line) {
if (preg_match("/.*-(.*)\s\d+/", $line, $array)) {
$array[1];
}
return -1;
}
function getIDFromLine($line) {
if (preg_match("/^(\d{1,3})-/", $line, $array)) {
$array[1];
}
return -1;
}
class Product {
public $id;
public $name;
function __construct($id, $name) {
$this->id = $id;
$this->name = $name;
}
}
我们的目的是将包含类似下面数据的文件转换为一个对象数组:
234-ladies_jumper 55
532-gents_hat 44
下面是客户端调用:
$line = getProductFileLines('log.txt');
$objects = array();
foreach ($lines as $line) {
$id = getIDFromLine($line);
$name = getNameFromLine($line);
$objects[$id] = getProductObjectFromId($id, $name);
}
我们需要调用所有的方法,代码也会和子系统紧紧耦合在一起。当子系统变化时,代码就会出现问题。而解决方法就是在子系统和客户端代码中引入一个入口。
10.3.2 实现
通过一个类为上面的过程式代码提供一个接口:
class ProductFacade {
private $products = array();
function __construct($file) {
$this->file = $file;
$this->compile();
}
private function compile() {
$lines = getProductFileLines($this->file);
foreach ($lines as $line) {
$id = getIDFromLine($line);
$name = getNameFromLine($line);
$this->products[$id] = getProductObjectFromId($id, $name);
}
}
function getProducts() {
return $this->products;
}
function getProduct($id) {
return $this->products[$id];
}
}
此时从客户端调用:
$facade = new ProductFacade('log.txt');
$facade->getProduct(234);
10.3.3 效果
外观模式是一个十分简单的概念,它只是为一个分层或一个子系统创建一个单一的入口。其优点在于:
- 有助于分离项目中的不同部分。
- 开发者在客户端调用变得简单。
- 降低了代码和子系统的耦合性。
外观模式对封装过程式代码块十分有利,是一个对象封装器。