首 页   · 站长博客 · 用户注册 · 会员登陆  · 会员排行  ·最新主题  ·最近回复  精华区  版权声明  ·论坛管理
  当前登录身份:游客,请先登录。  笔名: 口令: 验证码:   
楼 主  index »  PHP相关资源下载区 » 《PHP设计模式介绍》第十一章 代理模式(2)  


  作者:lxshark
  注册时间:2009-08-06
  主题/回复:49/50
  积分:639
  等级:★★★(六级)
  称号:声名鹊起

  lxshark@yeah.net..
  593993745
  hi.baidu.com/mythicsky

 

 发表:2009-08-08 20:54:27 阅读 1978 次 回复 1 次 得分0  |   字号 字色
《PHP设计模式介绍》第十一章 代理模式(2)
因为WeatherReport实际上并不是你程序中定义的类, SoapClient都象stdClass的实例化一样的返回所有的对象。这时你也可以获得返回对象的属性的值。


class ProxyTestCase extends UnitTestCase {
function TestGetWeatherReport() {
$moline_weather = $this->client->getWeatherReport(‘KMLI’);
$this->assertIsA($moline_weather, ‘stdClass’);
$weather_tests = array(
‘timestamp’ => ‘String’
,’station’ => ‘stdClass’
,’phenomena’ => ‘Array’
,’precipitation’ => ‘Array’
,’extremes’ => ‘Array’
,’pressure’ => ‘stdClass’
,’sky’ => ‘stdClass’
,’temperature’ => ‘stdClass’
,’visibility’ => ‘stdClass’
,’wind’ => ‘stdClass’
);
foreach($weather_tests as $key => $isa) {
$this->assertIsA($moline_weather->$key,
$isa,
“$key should be $isa, actually [%s]”);
}
}
}
 
上面的代码创建了属性和返回类型的映射。你可以迭代这些预期值的列表,并使用assertIsA()验证正确的类型。当然你以可以同样的验证其他的集合对象。 


class ProxyTestCase extends UnitTestCase {
function TestGetWeatherReport() {
// continued 。。。
$temp = $moline_weather->temperature;
$temperature_tests = array(
‘ambient’ => ‘Float’
,’dewpoint’ => ‘Float’
,’relative_humidity’ => ‘Integer’
,’string’ => ‘String’
);
foreach($temperature_tests as $key => $isa) {
$this->assertIsA($temp->$key,
$isa,
“$key should be $isa, actually [%s]”);
}
}



上面的方法输出的实际效果如下:


stdClass Object
(
[timestamp] => 2005-02-27T13:52:00Z
[station] => stdClass Object
(
[icao] => KMLI
[wmo] => 72544
[iata] =>
[elevation] => 179
[latitude] => 41。451
[longitude] => -90。515
[name] => Moline, Quad-City Airport
[region] => IL
[country] => United States
[string] => KMLI - Moline, Quad-City Airport, IL, United States @ 41。451’N -90。515’W 179m
)
// 。。。
[temperature] => stdClass Object
(
[ambient] => 0。6
[dewpoint] => -2。8
[relative_humidity] => 78
[string] => 0。6c (78% RH)
)
// 。。。
)
 
延迟代理 

现在你基本掌握了PHP5风格的SoapClient(如何做一个远程代理),但是你怎么才能写一个延迟实例化的代理给SoapClient呢?


class GlobalWeather {
private $client;
// ‘Station getStation(string $code)’, public function getStation($code) {
return $this->client->getStation($code);
}



getStation()可以代理$client变量指向的getStation()方法。不管如何,从这点上看, SoapClient实例并没有创建,也没有存储到$client变量,因为上面已说过,对WSDL文件进行远程处理应该延迟到真正需要的时候。

你可以在插入一段延迟加载的代码之前做一下client的调用,来延迟SoapClient的实例化


class GlobalWeather {
private $client;
private function lazyLoad() {
if (! $this->client instanceof SoapClient) {
$this->client = new SoapClient(
‘http://live。capescience。com/wsdl/GlobalWeather。wsdl’);
}
}
// ‘Station getStation(string $code)’, public function getStation($code) {
$this->lazyLoad();
return $this->client->getStation($code);
}



lazyLoad()中创建SoapClient对象是一定要的。这里存在一个问题:如果我是一个懒惰的编码者,让我非常不爽是:我不得不在所有的代理方法中加入$this->lazyLoad();。有更加简便的方法吗?当然有,重写一遍吧,使用PHP5新的特性来返回对象。改lazyLoad()的名字为client(),并在这个方法里面实例化$client,代理中的方法访问client()方法优于访问$client属性。把延迟实例化做的更加简单!

class GlobalWeather {
private function client() {
if (! $this->client instanceof SoapClient) {
$this->client = new SoapClient(
‘http://live。capescience。com/wsdl/GlobalWeather。wsdl’);
}
return $this->client;
}
// 。。。
// ‘boolean isValidCode(string $code)
public function isValidCode($code) {
return $this->client()->isValidCode($code);
}
// and so on for other SOAP service methods 。。。
// ‘WeatherReport getWeatherReport(string $code)
public function getWeatherReport($code) {
return $this->client()->getWeatherReport($code);
}




你迷上GlobalWeather服务的延迟实例代理类了吗?你有一个类可以在任何时间在你的程序里面创建,并且在不需要它们的时候就不解析的远程资源。使用代理类还有另外一个优势:使用代理可以列举SOAP所支持的方法,你现在就可以对这个类进行测试。

注:延迟代理可延迟异常

在PHP5里,创建一个对象会产生一个异常。使用延迟实例化代理,你可以延迟这个潜在的异常直到第一次使用方法创建对象的时候。。(你可以试试用代理完成这个功能。)这明显不是代理模式的重点,但是往往有一些事情,你需要记住。

动态代理

PHP5提供一些很好的特性,可以快速的封装一个代理类而不用明确的写出每一个方法。


class GenericProxy {
protected $subject;
public function __construct($subject) {
$this->subject = $subject;
}
public function __call($method, $args) {
return call_user_func_array(
array($this->subject, $method),
$args);
}




这里的关键是_call()方法(通过EXPERIMENTAL扩展重载,同样可用于PHP4)。代理类中的_call方法允许你通过$subject代替的方式来重定义每个调用。由于__call()比其他方法的优先级别都低,你可以在代理类中定义一个方法,让__call()来代替执行,于是你可以加一些特别的需求到你使用的代理模式。

总结

代理模式在很多情况下都非常有用,特别是你想强行控制一个对象的时候,比如:延迟加载,监视状态变更的方法等等。这章通过开发GlobalWeather类做示范,以后你也可以使用代理模式在你的本地计算机上使用远程资源:

  


动态代理在编写代码的时候非常简单,因此可以很快速和容易的实现在你的程序中。然而(所有的实现都依赖__call()方法),反射并不能在对象内部具备这样的可见性(译注:反射一般是在不了解类的内部情况下操作的)。在特殊情况下,如果你使用一个代理就需要一个接口,你不能老是依赖于__call()方法,至少必须编码的时候,应该把接口所有的方法很明确的写入你的代理类。
 
 
 1#楼  
 
  回复人:lxshark
  注册时间:2009-08-06
  主题/回复:49/50
  积分:639
  等级:★★★(六级)
  称号:声名鹊起

   
 1#楼 发表于2009-08-08 21:32:50  评分:× 

回复给楼主(lxshark)
希望能对你们更好的学习PHP有所帮助,也期望大家有时间多光顾我的
点此打开链接:http://hi.baidu.com/mythicsky
  页数1/1首页 « 1 » 末页
  发表回复:您还没有登陆,无法发表回复。请先[登陆]

一起PHP技术联盟 主办:一起PHP 联系方式:站长QQ:4304410 QQ群:8423742 20159565 站长博客 E-mail: nqp@nqp.me 执行时间:0.009sec
SimsmaBBS 2008 (v6.0) Developed by 17php.com,Copyright(C)2003-2010 All rights reserved. 副本授权:一起PHP官方专用版