资讯专栏INFORMATION COLUMN

Laravel学习笔记之Decorator Pattern

dendoink / 1267人阅读

摘要:把和拼接在一起的场所是,所以需要造一个类,在其内部实现对的操作中实现了把原有的进过个的装饰后得到的新的,新的还是的实现,还是原来的物种。

说明:Laravel中Middleware的实现主要利用了Decorator Pattern的设计,本文主要先学习下Decorator Pattern如何实现,为后面学习Middleware的设计做个铺垫。Decorator Pattern和Adapter Pattern会有很多相似之处,但相比较于Adapter Pattern重点突出adapter,Decorator Pattern重点突出的是wrapper,两个不是同一概念。

开发环境:Laravel5.3 + PHP7 + OS X 10.11

Decorator Pattern

Decorator Pattern作为一种结构型模式,可以给现有对象Component装饰decorate几个feature,而不影响原有的Component对象,这几个feature就是装饰对象Decorator。这种设计很好用,因为可以随时增加或减少想要的feature,并且增加或减少这种操作又很简单,实现了程序松耦合。就像Laravel中每一个middleware就是一个feature,如果想要增加一个不缓存request的feature,可以增加一个middleware假设叫做NoCacheMiddleware,写好后只需要在app/Http/Kernel.php文件中添加下配置就可。看下一个简单的demo实例,看看如何使用Decorator Pattern。
先定义一个IMiddleware的接口,保证设计的features都是同一物种,即只有实现了该接口的feature才称为middleware:

namespace MyRightCapitalDevelopmentDecoratorPattern;

interface IMiddleware
{
    public function handle();
}

在该接口中定义一个handle()函数,每一个feature必须实现这个handle()来做逻辑。现在需要设计5个features,并且每一个feature都必须是middleware:

$features = [
    CheckForMaintenanceMode::class,
    AddQueuedCookiesToResponse::class,
    StartSession::class,
    ShareErrorsFromSession::class,
    VerifyCsrfToken::class,
];

OK,现在实现第一个feature,并改造为middleware:

namespace MyRightCapitalDevelopmentDecoratorPattern;

class CheckForMaintenanceMode implements IMiddleware
{
    /**
     * @var MyRightCapitalDevelopmentDecoratorPatternIMiddleware
     */
    private $middleware;

    /**
     * CheckForMaintenanceMode constructor.
     *
     * @param MyRightCapitalDevelopmentDecoratorPatternIMiddleware $middleware
     */
    public function __construct(IMiddleware $middleware)
    {
        $this->middleware = $middleware;
    }
    
    public function handle()
    {
        echo "Check if the application is in the maintenance status." . PHP_EOL;
        $this->middleware->handle();
    }
}

第一个middleware是CheckForMaintenanceMode,需要检查程序是否处于维护模式。实现第二个feature,并改造为middleware:

namespace MyRightCapitalDevelopmentDecoratorPattern;

class AddQueuedCookiesToResponse implements IMiddleware
{
    /**
     * @var MyRightCapitalDevelopmentDecoratorPatternIMiddleware
     */
    private $middleware;

    /**
     * AddQueuedCookiesToResponse constructor.
     *
     * @param MyRightCapitalDevelopmentDecoratorPatternIMiddleware $middleware
     */
    public function __construct(IMiddleware $middleware)
    {
        $this->middleware = $middleware;
    }
    
    public function handle()
    {
        $this->middleware->handle();
        echo "Add queued cookies to the response" . PHP_EOL;
    }
}

第二个middleware实现把cookie添加到response。实现第三个feature,并改造为middleware:

namespace MyRightCapitalDevelopmentDecoratorPattern;

class StartSession implements IMiddleware
{
    /**
     * @var MyRightCapitalDevelopmentDecoratorPatternIMiddleware
     */
    private $middleware;

    /**
     * StartSession constructor.
     *
     * @param MyRightCapitalDevelopmentDecoratorPatternIMiddleware $middleware
     */
    public function __construct(IMiddleware $middleware)
    {
        $this->middleware = $middleware;
    }
    
    public function handle()
    {
        echo "Start session of this request." . PHP_EOL;
        $this->middleware->handle();
        echo "Close session of this request." . PHP_EOL;
    }
}

第三个feature主要实现开启和关闭session。实现第四个feature,并改造为middleware:

class ShareErrorsFromSession implements IMiddleware
{
    /**
     * @var MyRightCapitalDevelopmentDecoratorPatternIMiddleware
     */
    private $middleware;

    /**
     * ShareErrorsFromSession constructor.
     *
     * @param MyRightCapitalDevelopmentDecoratorPatternIMiddleware $middleware
     */
    public function __construct(IMiddleware $middleware)
    {
        $this->middleware = $middleware;
    }
    
    public function handle()
    {
        $this->middleware->handle();
        echo "Share the errors variable from request to the views." . PHP_EOL;
    }
}

第四个feature主要实现共享变量$errors,以便在视图中使用该变量。实现第五个feature,并改造为middleware:

namespace MyRightCapitalDevelopmentDecoratorPattern;

class VerifyCsrfToken implements IMiddleware
{
    /**
     * @var MyRightCapitalDevelopmentDecoratorPatternIMiddleware
     */
    private $middleware;

    /**
     * VerifyCsrfToken constructor.
     *
     * @param MyRightCapitalDevelopmentDecoratorPatternIMiddleware $middleware
     */
    public function __construct(IMiddleware $middleware)
    {
        $this->middleware = $middleware;
    }
    
    public function handle()
    {
        echo "Verify csrf token when post request." . PHP_EOL;
        $this->middleware->handle();
    }
}

第五个feature主要实现CSRF验证。OK,现在每一个feature都已经实现了,并将作为Decorator来装饰初始的Component。

OK,Decorator Pattern中已经有了五个Decorators,现在需要实现一个Component,然后用这五个Decorators来装饰Component。
现在定义一个Component接口,保证Component与Decorator是相似物种,并且Component又有自己的实现接口:

namespace MyRightCapitalDevelopmentDecoratorPattern;

interface IComponent extends IMiddleware
{
    public function getRequest();
}

现在构造一个Component:

namespace MyRightCapitalDevelopmentDecoratorPattern;

class Request implements IComponent
{
    public function handle()
    {
        echo "This is a request from the client. And this request will go through the middlewares." . PHP_EOL;
    }

    public function getRequest()
    {
        return $this;
    }
}

OK,在Decorator Pattern中,目前已经构造好了Component和Decorator。把Component和Decorator拼接在一起的场所是Client,所以需要造一个Client类,在其内部实现对Component的Decorate操作:

namespace MyRightCapitalDevelopmentDecoratorPattern;

class Client
{
    /**
     * @var MyRightCapitalDevelopmentDecoratorPatternRequest
     */
    protected $request;

    /**
     * @var MyRightCapitalDevelopmentDecoratorPatternIMiddleware
     */
    protected $response;

    public function __construct()
    {
        // Component
        $this->request  = new Request();
        
        // Decorate the Component
        $this->response = $this->wrapDecorator($this->request);
    }

    /**
     * @param MyRightCapitalDevelopmentDecoratorPatternIMiddleware $decorator
     *
     * @return MyRightCapitalDevelopmentDecoratorPatternIMiddleware
     */
    public function wrapDecorator(IMiddleware $decorator)
    {
        $decorator = new VerifyCsrfToken($decorator);
        $decorator = new ShareErrorsFromSession($decorator);
        $decorator = new StartSession($decorator);
        $decorator = new AddQueuedCookiesToResponse($decorator);
        $response  = new CheckForMaintenanceMode($decorator);

        return $response;
    }

    /**
     * @return MyRightCapitalDevelopmentDecoratorPatternIMiddleware
     */
    public function getResponse()
    {
        return $this->response->handle();
    }
}

Client中wrapDecorator()实现了把原有的Component进过5个Middlewares的装饰后得到的新的Component,新的Component还是IMiddleware的实现,还是原来的物种。整个UML图:

OK,现在执行整个Decorator Pattern,看看是不是这些middlewares已经被装饰进原来的Component,创建一个index.php文件:

// 加载composer的autoload.php文件
include __DIR__ . "/../../../vendor/autoload.php";

$client = new MyRightCapitalDevelopmentDecoratorPatternClient();
$client->getResponse();

php index.php文件看看输出什么:

Check if the application is in the maintenance status.
Start session of this request.
Verify csrf token when post request.
This is a request from the client. And this request will go through the middlewares.
Share the errors variable from request to the views.
Close session of this request.
Add queued cookies to the response.

的确,五个middlewares已经装饰了原有的component,并检查下装饰次序是否是正确的?
实际上,Client中的$this->response等同于:

$response = new CheckForMaintenanceMode(
                new AddQueuedCookiesToResponse(
                    new StartSession(
                        new ShareErrorsFromSession(
                            new VerifyCsrfToken(
                                new Request()
                        )
                    )
                )
            )
        );

所以,执行次序是:

1. CheckForMaintenanceMode::handle() -> 先执行 echo "Check if the application is in the maintenance status.", 然后执行 AddQueuedCookiesToResponse::handle()
2. AddQueuedCookiesToResponse::handle() -> 先执行 StartSession::handle(), 然后执行 echo "Add queued cookies to the response."
3. StartSession::handle() -> 先执行 echo "Start session of this request.", 然后执行 ShareErrorsFromSession::handle(), 最后执行 echo "Close session of this request."
4. ShareErrorsFromSession::handle() -> 先执行VerifyCsrfToken::handle(), 然后执行 echo "Share the errors variable from request to the views."
5. VerifyCsrfToken::handle() -> 先执行 echo "Verify csrf token when post request.", 然后执行 Request::handle()
6. Request::handle() -> 执行 echo "This is a request from the client. And this request will go through the middlewares."

// So,执行顺序等同于:
echo "Check if the application is in the maintenance status." -> 
echo "Start session of this request." -> 
echo "Verify csrf token when post request." -> 
echo "This is a request from the client. And this request will go through the middlewares." ->
echo "Share the errors variable from request to the views." ->
echo "Close session of this request." ->
echo "Add queued cookies to the response." ->

在Laravel里每一个Middleware中有前置操作和后置操作。在本demo里echo语句前置于$this->middleware->handle();则为前置操作,后置则为后置操作。
OK,再加一个Kernel类,保证Request经过Middleware的前置操作后进入Kernel,然后从Kernel出来进入Middlewares的后置操作,一步步过滤:

namespace MyRightCapitalDevelopmentDecoratorPattern;

interface IKernel extends IMiddleware
{

}

class Kernel implements IKernel
{
    public function handle()
    {
        echo "Kernel handle the request, and send the response." . PHP_EOL;
    }
}

// 修改Request
class Request implements IRequest
{
    /**
     * @var MyRightCapitalDevelopmentDecoratorPatternIKernel
     */
    private $kernel;

    public function __construct(IKernel $kernel)
    {
        $this->kernel = $kernel;
    }

    public function handle()
    {
        echo "This request has been filtering by the before action in the middlewares, and go into the kernel." . PHP_EOL;
        $this->kernel->handle();
        echo "The request has been handled by the kernel, and will be send to the after action in the middlewares" . PHP_EOL;
    }

    public function getRequest()
    {
        return $this;
    }
}

// 修改下Client的构造函数
public function __construct()
    {
        // Component
        $this->request = new Request(new Kernel());

        // Decorate the Component
        $this->response = $this->wrapDecorator($this->request);
    }

则再次执行index.php文件,得到:

Check if the application is in the maintenance status.
Start session of this request.
Verify csrf token when post request.
This request has been filtering by the before action in the middlewares, and go into the kernel.
Kernel handle the request, and send the response.
The request has been handled by the kernel, and will be send to the after action in the middlewares
Share the errors variable from request to the views.
Close session of this request.
Add queued cookies to the response.

具体流程上文已经讨论,可画一张草图展示处理流程,其中Before表示该Middleware的前置操作,After表示该Middleware的后置操作:

OK,使用Decorator Pattern来层层过滤Request,并实现分层,最后进入Kernel执行得到Response,然后Response经过层层过滤,返回给客户端。非常赞的设计。

总结:本文主要学习Laravel如何使用Decorator Pattern来设计Middleware。下一篇学习下Laravel中Middleware的源码。

欢迎关注Laravel-China。

RightCapital招聘Laravel DevOps

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/30438.html

相关文章

  • Laravel学习笔记Middleware源码解析

    摘要:学习笔记之已经聊过使用了来设计,看源码发现其巧妙用了和的一些数组函数来设计。开发环境内置函数和看源码之前,先看下这几个内置函数的使用。学习笔记之实例化源码解析已经聊过的实例化,得到中的变量,即的实例化对象。后面再学习下的源码,到时见。 说明:本文主要学习Laravel的Middleware的源码设计思想,并将学习心得分享出来,希望对别人有所帮助。Laravel学习笔记之Decorato...

    _Dreams 评论0 收藏0
  • Laravel学习笔记Filesystem源码解析(下)

    摘要:源码解析这个类的源码主要就是文件的操作和文件属性的操作,而具体的操作是通过每一个实现的,看其构造函数看以上代码知道对于操作,实际上是通过的实例来实现的。可以看下的使用上文已经说了,使得对各种的操作变得更方便了,不管是还是得。 说明:本文主要学习下LeagueFlysystem这个Filesystem Abstract Layer,学习下这个package的设计思想和编码技巧,把自己的一...

    Luosunce 评论0 收藏0
  • Decorator Pattern With Laravel 装饰者模式

    摘要:装饰对象包含一个真实对象的引用装饰对象接受所有来自客户端的请求。装饰对象可以在转发这些请求以前或以后增加一些附加功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。 Decorator Pattern 装饰者模式 纲要: 1. 一个初学者的疑惑 2. 装饰者模式的特点 3. 简单case掌握装饰者模式 4. laravel中装饰者模式的应用 Con...

    roundstones 评论0 收藏0
  • laravel 框架关键技术解析》学习笔记装饰者模式

    摘要:装饰者模式是在开放关闭原则下实现动态添加或减少功能提高程序的扩展性详细介绍注本文可以作为学习装饰者模式的基础篇但是我个人更建议配套装饰者模式来学习效果更佳本文中的例子是由框架关键技术解析中摘抄的。 装饰者模式:是在开放-关闭原则下实现动态添加或减少功能,提高程序的扩展性.详细介绍注: 本文可以作为学习装饰者模式的基础篇,但是我个人更建议配套Decorator Pattern With...

    fuyi501 评论0 收藏0
  • Laravel学习笔记Core Concepts in Guzzle Package——Strea

    摘要:使用了来表示该,该接口也是对的抽象,暴露了一些常用方法判断是否满足要求的方法的读写相关操作获取元数据方法操作指针相关方法等等。本篇主要学习下相关使用。后续还会分享相关使用,到时见。 说明:本文主要学习guzzlehttp/guzzle package的使用,该package提供了一套发送HTTP请求API,就像phpunit package, mockery package, symf...

    singerye 评论0 收藏0

发表评论

0条评论

dendoink

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<