代理,百度百科解释道:以他人的名义,在授权范围内进行对被代理人直接发生法律效力的法律行为。
玩游戏的同学对代练肯定不陌生,找代练帮忙升级打怪,下面就以此例编写个简单的代理模式:
结构如图,首先定义一个游戏接口,接口有游戏的一些基本动作,如登录,打怪,升级。再定义一个玩家的类,继承游戏接口,实现动作。最后定义一个代理类,客户端通过调用代理实际完成玩家的动作。
?
class="php" name="code"><?php interface IGamePlayer{ public function login( $username, $password ); public function killBoss(); public function upgrade(); } class GamePlayer implements IGamePlayer{ private $username = ''; public function __construct( $username ) { $this->username = $username; } public function login( $username, $password ) { echo $username."登录成功\n"; } public function killBoss() { echo $this->username."在打怪!\n"; } public function upgrade() { echo $this->username."升级了!\n"; } } class GamePlayerProxy implements IGamePlayer{ private $gamePlayer = null; public function __construct( $gamePlayer ) { $this->gamePlayer = $gamePlayer; } public function login( $username, $password ) { $this->gamePlayer->login( $username, $password ); } public function killBoss() { $this->gamePlayer->killBoss(); } public function upgrade() { $this->gamePlayer->upgrade(); } } $player = new GamePlayer( 'luoxin' ); $proxy = new GamePlayerProxy( $player ); $proxy->login( 'luoxin', 'password' ); $proxy->killBoss(); $proxy->upgrade(); ?> 运行结果: luoxin登录成功 luoxin在打怪! luoxin升级了! [Finished in 0.2s]
?
?
代理模式的定义:
为其他对象提供一种代理以控制对这个对象的访问。代理模式也叫委托模式,它是一项基本设计技巧。许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式,而且在日常的应用中,代理模式可以提供非常好的访问控制。代理模式中三个角色的定义:
1、抽象主题角色(例中的IGamePlayer)
抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。
2、具体主题角色(例中的GamePlayer)
也叫做被委托角色,被代理角色。它才是冤大头,也是业务逻辑的具体执行者。
3、代理主题角色(例中的GamePlayerProxy)
也叫委托类,代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。
?
代理模式的优点:
1、职责清晰
真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事情,附带结果就是编程简洁清晰。
2、高扩展性
具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。
3、智能化
见下文的动态代理
?
?
代理模式的扩展:
?
1、普通代理
客户端只能访问代理角色,而不能访问真实角色。
<?php
interface IGamePlayer{
public function login( $username, $password );
public function killBoss();
public function upgrade();
}
class GamePlayer implements IGamePlayer{
private $username = '';
public function __construct( $proxy, $username ) {
if ( !is_object( $proxy ) || get_class( $proxy ) != 'GamePlayerProxy' ) {
try{
throw new Exception( "不能创建真实角色" );
}catch ( Exception $e ) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
}else
$this->username = $username;
}
public function login( $username, $password ) {
echo $username."登录成功\n";
}
public function killBoss() {
echo $this->username."在打怪!\n";
}
public function upgrade() {
echo $this->username."升级了!\n";
}
}
class GamePlayerProxy implements IGamePlayer{
private $gamePlayer = null;
public function __construct( $username ) {
$this->gamePlayer = new GamePlayer( $this, $username );
}
public function login( $username, $password ) {
$this->gamePlayer->login( $username, $password );
}
public function killBoss() {
$this->gamePlayer->killBoss();
}
public function upgrade() {
$this->gamePlayer->upgrade();
}
}
$proxy = new GamePlayerProxy( 'luoxin' );
$proxy->login( 'luoxin', 'password' );
$proxy->killBoss();
$proxy->upgrade();
?>
运行结果:
luoxin登录成功
luoxin在打怪!
luoxin升级了!
[Finished in 0.1s]
?运行结果完全相同。在该模式下,调用者只知代理而不知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色想怎么修改就怎么修改,对高层次的模块没有任何影响,只要你实现了接口所对应的方法,该模式扩展性要求较高的场合。
?
?
2、强制代理
必须通过真实角色查找到代理角色,否则你不能访问。
<?php
interface IGamePlayer{
public function login( $username, $password );
public function killBoss();
public function upgrade();
public function getProxy();//新增一个获取代理的方法
}
class GamePlayer implements IGamePlayer{
private $username = '';
private $proxy = null;
public function __construct( $username ) {
$this->username = $username;
}
//实现获取本身代理类
public function getProxy() {
$this->proxy = new GamePlayerProxy( $this );
return $this->proxy;
}
public function login( $username, $password ) {
if ( $this->isProxy() )
echo $username."登录成功\n";
else
echo "请使用指定的代理访问\n";
}
public function killBoss() {
if ( $this->isProxy() )
echo $this->username."在打怪!\n";
else
echo "请使用指定的代理访问\n";
}
public function upgrade() {
if ( $this->isProxy() )
echo $this->username."升级了!\n";
else
echo "请使用指定的代理访问\n";
}
//新增判断是否有代理的私有方法
private function isProxy() {
return !( $this->proxy == null );
}
}
class GamePlayerProxy implements IGamePlayer{
private $gamePlayer = null;
public function __construct( $gamePlayer ) {
$this->gamePlayer = $gamePlayer;
}
public function login( $username, $password ) {
$this->gamePlayer->login( $username, $password );
}
public function killBoss() {
$this->gamePlayer->killBoss();
}
public function upgrade() {
$this->gamePlayer->upgrade();
}
public function getProxy(){
return $this;
}
}
$player = new GamePlayer( 'luoxin' );
$player->killBoss();
$proxy = $player->getProxy();
$proxy->login( 'luoxin', 'password' );
$proxy->killBoss();
$proxy->upgrade();
?>
运行结果:
请使用指定的代理访问
luoxin登录成功
luoxin在打怪!
luoxin升级了!
[Finished in 0.1s]
?记住:强制代理就是要从真实角色查找到代理角色,不允许直接访问真实角色。高层模块只要调用getProxy()就可以访问真实角色的所有方法,它根本就不需要产生一个代理出来,代理的管理已经由真实角色自己完成。
?
?
3、代理是有个性的
一个类可以实现多个接口,完成不同任务的整合。也就是说代理类不仅仅可以实现主题接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截和过滤。例如我们找人代练是要交钱的,假设升一级需要250$(好贵啊),这个计算功能就是代理类的个性,它应该在代理的接口中定义。
如图,新增一个IProxy接口,接口中有个计费的方法count,代理类GamePlayerProxy继承IGamePlayer接口的同时也继承IProxy
?
<?php interface IGamePlayer{ public function login( $username, $password ); public function killBoss(); public function upgrade(); } interface IProxy{ public function count(); } class GamePlayer implements IGamePlayer{ private $username = ''; public function __construct( $username ) { $this->username = $username; } public function login( $username, $password ) { echo $username."登录成功\n"; } public function killBoss() { echo $this->username."在打怪!\n"; } public function upgrade() { echo $this->username."升级了!\n"; } } class GamePlayerProxy implements IGamePlayer,IProxy{ private $gamePlayer = null; public function __construct( $gamePlayer ) { $this->gamePlayer = $gamePlayer; } public function login( $username, $password ) { $this->gamePlayer->login( $username, $password ); } public function killBoss() { $this->gamePlayer->killBoss(); } public function upgrade() { $this->gamePlayer->upgrade(); $this->count(); } public function count() { echo "升级总费用是:250$\n"; } } $player = new GamePlayer( 'luoxin' ); $proxy = new GamePlayerProxy( $player ); $proxy->login( 'luoxin', 'password' ); $proxy->killBoss(); $proxy->upgrade(); ?> 运行结果: luoxin登录成功 luoxin在打怪! luoxin升级了! 升级总费用是:250$ [Finished in 0.2s]
?
?
4、虚拟代理
听起来复杂,其实就是在代理类中每个方法中判断主题对象是否被创建,如果没有,则创建。可以避免被代理对象较多而引起的初始化缓慢的问题。
?
5、动态代理
动态代理在JAVA中有充分的应用,其叼炸天的AOP编程用的就是此模式。在PHP中,用魔术方法__call可以轻松地实现。
?
<?php interface IGamePlayer{ public function login( $username, $password ); public function killBoss(); public function upgrade(); } class GamePlayer implements IGamePlayer{ private $username = ''; public function __construct( $username ) { $this->username = $username; } public function login( $username, $password ) { echo $username."登录成功\n"; } public function killBoss() { echo $this->username."在打怪!\n"; } public function upgrade() { echo $this->username."升级了!\n"; } } class GamePlayerProxy{//此处不能再继承IGamePlayer接口 private $gamePlayer = null; public function __construct( $gamePlayer ) { $this->gamePlayer = $gamePlayer; } public function __call($method,$args){ //方法前后可以做很多智能化的处理 return call_user_func_array(array($this->gamePlayer, $method), $args); } } $player = new GamePlayer( 'luoxin' ); $proxy = new GamePlayerProxy( $player ); $proxy->login( 'luoxin', 'password' ); $proxy->killBoss(); $proxy->upgrade(); ?> 运行结果: luoxin登录成功 luoxin在打怪! luoxin升级了! [Finished in 0.1s]
?
?
?