- 作用 不改变原对象,动态的给一个对象添加/修改功能。 为什么不直接用继承? 下面代码我们实际上是要给RequestHand2 hand增加新的功能。
<?php
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
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)
{
$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来包装中间件,最后统一调用,这样就不用写多重嵌套了。
|