- 作用
不改变原对象,动态的给一个对象添加/修改功能。
为什么不直接用继承?
下面代码我们实际上是要给RequestHand2 hand增加新的功能。
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2021/9/28
* Time: 23:13
*/
//请求类
class Request
{
}
//抽象类 请求处理类
abstract class RequestHand
{
//处理请求
abstract function hand(Request $request);
}
//正常请求处理
class RequestHand2 extends RequestHand
{
function hand(Request $request)
{
echo "正常处理\n";
}
}
//加一个验证请求
class Auth extends RequestHand2
{
function hand(Request $request)
{
echo "验证请求\n";
parent::hand($request);
}
}
//加一个记录日志功能
class Log extends RequestHand2
{
function hand(Request $request)
{
parent::hand($request);
echo "记录日志\n";
}
}
//请求
$request = new Request();
//正常处理就可以这么写
(new RequestHand2())->hand($request);
//我对请求过滤 我要验证请求
(new Auth())->hand($request);
// 我要记录日志
(new Log())->hand($request);
上述代码就会出现这样一个问题,如何我又要验证请求又要记录日志怎么办,那么我只能再加一个类。
class AuthAndLog extends RequestHand2
{
function hand(Request $request)
{
echo "验证请求\n";
parent::hand($request);
echo "记录日志\n";
}
}
或者
class AuthAndLog extends Log
{
function hand(Request $request)
{
echo "验证请求\n";
parent::hand($request);
}
}
很明显,这样处理会出现重复代码或者造成许多层继承的问题,这还是只有两个功能,如果有若干个,代码冗余直接爆炸。
- 装饰器模式实现
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2021/9/28
* Time: 23:13
*/
//请求类
class Request
{
}
//抽象类 请求处理类
abstract class RequestHand
{
//处理请求
abstract function hand(Request $request);
}
//正常请求处理
class RequestHand2 extends RequestHand
{
function hand(Request $request)
{
echo "正常处理\n";
}
}
//装饰抽象类
abstract class RequestHandDecorate extends RequestHand{
protected $requestHand;
public function __construct(RequestHand $rh)//注入RequestHand类 这是重点
{
$this->requestHand=$rh;
}
}
//加一个验证请求
class Auth extends RequestHandDecorate
{
function hand(Request $request)
{
echo "验证请求\n";
$this->requestHand->hand($request);
}
}
//加一个记录日志功能
class Log extends RequestHandDecorate
{
function hand(Request $request)
{
$this->requestHand->hand($request);
echo "记录日志\n";
}
}
//请求
$request = new Request();
//正常处理就可以这么写
(new RequestHand2())->hand($request);
echo "---------\n";
//我对请求过滤 我要验证请求
(new Auth(new RequestHand2()))->hand($request);
echo "---------\n";
// 我要记录日志
(new Log(new RequestHand2()))->hand($request);
echo "---------\n";
//我又要记录又要验证
(new Auth(new Log(new RequestHand2())))->hand($request);
结果
这样的模型极具扩展性,可以非常轻松的添加新的装饰器并且随意的组合!
- 实际应用场景
- 上述代码实际上就实现了一个简单的中间件,但是这样写的话,如果有多个处理,会写许多嵌套。可以参考laravel的实现。laravel装饰类不是从构造函数注入实例,而是从方法传入闭包,再使用arrary_reduce来包装中间件,最后统一调用,这样就不用写多重嵌套了。