php设计模式之Proxy(代理模式)和Facade(外观)设计模式
Proxy(代理模式)和Facade(外观)设计模式
它们均为更复杂的功能提供抽象化的概念,但这两种实现抽象化的过程大不相同
Proxy案例中,所有的方法和成员变量都来自于目标对象,必要时,该代理能够对它所传递的数据进行修改或检查
魔术方法使得Proxy的实现变的简单,Proxy模式的一类应用时用来记录方法的访问信息
还可以利用Proxy的类确定代码的范围或调试程序中存在的问题
<?php class LoggingProxy{ private $target; //传递进去一个对象 public function __construct($target){ $this->target=$target; } protected function log($line){ echo "[".$line."]\r\n"; } public function __set($name,$value){ $this->target->$name=$value; $this->log("setting value for $name:$value "); } public function __get($name){ $value=$this->target->$name; $this->log("Getting value for $name:$value"); return $value; } public function __isset($name){ $value=isset($this->target->$name); $this->log("checking isset for $name".($value?"true":"false")); return $value; } public function __call($name,$arguments){ $this->log("calling method $name width:".implode(",", $arguments)); return call_user_func_array(array($this->target,$name), $arguments); } } class People{ public $name='hk'; public $age; public function sayname(){ return $this->name; } public function plus($a,$b){ return $a+$b; } } $p=new People(); $proxy=new LoggingProxy($p); echo $proxy->name;//hk echo "<br>"; $proxy->age=10; echo $p->age;//10 echo "<br>"; echo $proxy->sayname(); echo "<br>"; echo $proxy->plus(2,3);
结果
[Getting value for name:hk] hk
[setting value for age:10 ] 10
[calling method sayname width:] hk
[calling method plus width:2,3] 5
多数情况下Proxy不应该改变它所代理的类的行为
Proxy与它所代理的在类型上不完全一致,这也是其一个缺点。
因而若需要进行类型提示或代码检查以确保对象是某一特定类型,这种情况下就不能使用代理模式
Facade(外观)模式提供了不同的功能,用来抽象化复杂功能,以使应用程序无需了解子系统处理
各请求的细节,就能完成整个处理过程
例如:处理典型api请求时,用户需要通过子系统进行认证,认证通过后,请求通过api子系统传递给远程服务器
处理,最后通过其它api的函数对相应解码
Facade方法粗略实现如下
<?php class Facade{ public function apiRequestJson($method,$parameters){ $user=User::getAuthenticatedUser(); if($user->hasPermission($method)){ $result=$this->api->$method($parameters); return json_encode($result); } } }
Facade并不为子系统添加任何新的功能,而是为子系统委托合适的责任。
子系统无需知道Facade的存在,而应用程序也无需知道子系统的存在。
下面代码,没使用外观模式前
<?php 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)){ return str_replace('_', ' ', $array[1]); } return ''; } function getIdFromLine($line){ if(preg_match('#^(\d{1,3})-#', $line,$arr)){ return $arr[1]; } return -1; } class Product{ public $id; public $name; public function __construct($id,$name){ $this->id=$id; $this->name=$name; } } $lines=getProductFileLines('t.txt'); $objects=array(); foreach ($lines as $line){ $id=getIdFromLine($line); $name=getNameFromLine($line); $objects[$id]=getProductObjectFromId($id, $name); }
t.txt内容
234-ladies_jumper 55 532-gents_hat 44
如果像以上代码调用子系统,我们的代码和子系统紧紧耦合在一起,当子系统变化时,或者我们决定将其与子系统完全断开时,代码就会出问题,所以我们需要在
这些子系统和代码中引入一个入口。
class ProductFacade{ private $products=array(); public 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); } } public function getProducts(){ return $this->products; } public function getProduct($id){ return $this->products[$id]; } } $facade=new ProductFacade('t.txt'); echo $facade->getProduct(234)->name;