委托模式
是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。委托模式是一项基本技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式。委托模式的核心要点
- 有两个对象参与处理同一个请求。
- 把接受请求的对象,把请求委托另一个对象处理
举例说明适用场景
1、业务规模变大,原来电商网站只有普通规模。后来增加了用户类型VIP、积分商品结算、增加了合作用户。
2、或者其他非自己的团队开发相同类似的功能。同时进行
为什么要用委托模式?
第一点是解除耦合度,拿下面伪代码举例
class User extends XXXOOO
{
public $user_role;
//XXXXOOOO
//设置一个数据或方法,来区分用户状态
}
假如新增用户类型、那么会不断的更改主类或者继承的基类。主体的类会变得很大
代码栗子
User主类,以User为主。
namespace App\Delegation;
class User
{
public $name="启东";
public $gender="男";
public $user_id=0;
public $Delegation=null;
public function __construct($user_id)
{
$this->user_id=$user_id;
}
public function __call($name, $arguments)
{
if($this->Delegation!=null){
return call_user_func_array([$this->Delegation,$name],$arguments);
}
return false;
}
function getDiscount()
{
return 1;
}
}
继承了IUserDiscount
接口。
namespace App\Delegation;
interface IUserDiscount
{
function getUserDiscount();
}
UserVip.php
namespace App\Delegation;
class UserVip implements IUserDiscount
{
function getUserDiscount():float
{
return 0.8;
}
}
在最理想化的情况,所有开发团队都使用IUserDiscount接口。但也有可能不继承
namespace App\Delegation;
class UserCooperation
{
public function getCDiscount():float
{
return 0.7;
}
}
调用代码
require "Loader.php";
Loader::init();
use App\Delegation\Product;
use App\Delegation\User;
use App\Delegation\UserVip;
use App\Delegation\UserCooperation;
$product=new Product();
$user_id=101;
$product_id=1;
$user=new User($user_id);
$user->Delegation=new UserVip($user_id);
echo "商品的价格:".$product->getPrice($product_id)*$user->getUserDiscount();
echo PHP_EOL."<br/>".PHP_EOL;
$user->Delegation=new UserCooperation($user_id);
echo "商品的价格:".$product->getPrice($product_id)*$user->getCDiscount();
echo PHP_EOL."<br/>".PHP_EOL;
echo "文件加载数:".Loader::getLoadFileNum();
其他典型例子
例子一
class Color
{
function callColor()
{
print "Generate Red";
}
}
class ColorDelegator
{
private $targets;
function addObject($obj)
{
$this->target[] = $obj;
}
function __call($name, $args)
{
foreach ($this->target as $obj)
{
$r = new ReflectionClass($obj);
if ($method = $r->getMethod($name))
{
if ($method->isPublic() && !$method->isAbstract())
{
return $method->invoke($obj, $args);
}
}
}
}
}
$obj = new ColorDelegator();
$obj->addObject(new Color());
$obj->callColor();
例子二
class Bank{
protected $info;
/*
设置基本信息
@param string $type 类型。例如"RMB"
@param int $money 利率。例如"0.4%"
*/
public function updateBrankInfo($type,$money){
$this->info[$type]=$money;
}
/*
相关操作(包括存款、取款操作)
@param int $branktype 操作类型
*/
public function brankWithdraw($branktype){
$obj=new $branktype;
return $obj->brankMain($this->info);
}
}
/*
委托接口
*/
interface Delegate{
/*
操作方法:
实现该接口必须实现的方法
*/
public function brankMain($info);
}
/*
存款操作类
*/
class brankDeposit implements Delegate{
/*
存款操作
*/
public function brankMain($info){
echo $info['deposit'];
}
}
/*
取款操作类
*/
class brankWithdraw implements Delegate{
/*
取款操作
*/
public function brankMain($info){
echo $info['withdraw'];
}
}
/*
客户端测试代码:
*/
$bank=new Bank();
$bank->updateBrankInfo("deposit","4000");
$bank->updateBrankInfo("withdraw","2000");
$bank->brankWithdraw("brankDeposit");
echo "<br>";
$bank->brankWithdraw("brankWithdraw");
总结
传统模式
在传统方式下,我们需要判断当前操作是取款操作还是存款操作,在分别调用Bank类中的取款操作和存款操作。
委托模式
在委托模式下,我们将不需要客户端的判断操作,对客户端来说,需要什么操作,直接传入操作类型即可,Bank类可自动判断操作类型,返回相应操作的操作结果。当我们的操作类型非常多的时候,在客户端用if else判断无疑是很可怕的,再假如我们在很多地方都要有这块判断代码,我们需要对这些地方的判断代码都进行修改(加入后来添加的判断),而采用委托模式,我们仅仅需要在新添加的地方添加相应需要的类型即可,不需要改动其它地方的客户端代码(很大程度上提高了代码的复用性)。
委托模式意义在于委托两个字,并不一定要继承同一个接口,继承同一个接口那是理想型