设计模式历险记之装饰模式

装饰模式,动态地给一个对象添加一些额外地职责,就增加功能来说,装饰模式比生成子类更为灵活。

我们先来看看它的结构图:

Component 抽象接口提供了一个装饰模板,并且规定了一个装饰方法,ConcreteComponent 和 Decorator 都实现了这个接口, ConcreteComponent 是被装饰的对象,它本身能实现一些功能,并且它不知道任何的装饰器,也能被装饰器装饰实现功能的拓展, Decorator 是一个抽象的装饰器,它里面可能存在多个实体装饰器,ConcreteDecoratorA 和 ConcreteDecoratorB 都是具体的装饰器,提供具体的装饰方法。

应用场景:

这里我自己总结了两种应用的方式:

一种是在设计系统的某个小部分的时候,用装饰模式设计。

场景一:

公司需要自己去开发一个web框架,并将http请求处理交给了好学的你,这个时候装饰模式就可以大显身手了。

一个http请求到了框架后会经过层层的处理,最后将最终的结果返回给浏览器。

假如,我们这个 http 请求传过来的参数需要通过 token 认证,然后再过滤参数,然后通过请求的 url 来定位到方法,最后将方法执行的结果返回。

<?php
//装饰模板
interface Http{
    public function handle();
}

//被装饰的对象
class Request implements Http{
    public function handle(){
            echo '初始化的http请求!';
    }
}

//抽象装饰器
abstract class Decorator implements Http{
    protected $request;

    public function __construct(Http $request){
        $this->request = $request;
    }
}

//验证 token 装饰器
class checkToken extends Decorator{

    public function handle(){
        $this->request->handle();
        echo '进行token验证。';
    }
}

//过滤数据装饰器
class trim extends Decorator{
    public function handle(){
        $this->request->handle();
        echo '过滤请求数据';
    }
}

//寻找方法装饰器
class findMethod extends Decorator{
    public function handle(){
        $this->request->handle();
        echo '执行逻辑操作并返回';
    }
}

//一个新的 http 请求
$request = new Request();

//检查token
$request = new checkToken($request);

//过滤数据
$request = new trim($request);

//寻找方法
$request = new findMethod($request);

$request->handle();

看到上面的代码有些人可能会产生这样一个疑惑,为什么不把所有的装饰类的功能写到 request 请求类中,这样代码量也少,也简单易懂。

那我问你,假如你有一个 url 请求地址是公开的,不需要验证 token ,这个时候该怎么办按,傻眼了吧,去改 request 类,不行,改了后需要验证 token 的请求又不满足,拷贝一个不要验证 token 的 request 类,只有傻瓜才会这样做。

如果用装饰模式写出来,你只需要不去用验证 token 的装饰器去装饰你的 request 请求类就好了。

另外一种是系统存在,用到的某个对象不能完全满足需求,这个时候可以用装饰模式来实现。

场景二:

你现在碰到了一个需求,但是这个需求你以前曾经做过类似的,并且以前的代码你能复用,但是你需要在他代码上边加部分逻辑。这个时候你怎么做?首先,肯定不能改以前的代码,因为项目中其它地方有用到,其次,你说将它的代码拷贝一份,嗯,这样是能实现功能,但是代码维护要改多个地方,并显得很蠢。

如果是我,我会这样做:

<?php
<?php
class OldClass{
    public function handle(){
        echo '这个是老代码,但是新需求能用到。';
    }
}

class NewClass{
    public function handle($class){
        $class->handle();
        echo '我复用了老代码,并且添加了新的逻辑';
    }
}

$oldClass = new OldClass();

$NewClass = new NewClass();

$NewClass->handle($oldClass);

看了上面是不是对装饰模式有了一些感觉,好,下面我再用自己的话总结一下装饰模式:

装饰模式就是可以动态的给类或者是方法添加一些功能,它满足 OOP 中的开放关闭原则,它还能将类核心的功能和一些辅助功能给解耦,这样使得类更加灵活可用。

拓展:

其实上面实现装饰模式的方法还不是很优雅,我相信追求完美的你肯定不满足于此,那么,下面我们将继续拓展。

继续我们上面的场景一,我们用不同的方式来实现观察者模式:

  • 不用构造函数来实现:
<?php

class Request{
    public $str;

    public function handle(){
            $this->str = '初始化的http请求!';
    }
}

abstract class Decorator {
    public abstract function handle(Request $request);
}

class checkToken extends Decorator{
    public function handle(Request $request){
        $request->str = $request->str . '验证 token ';
        return $request;
    }
}

class trim extends Decorator{
    public function handle(Request $request){
        $request->str = $request->str . '过滤字符串';
        return $request;
    }
}

class findMethod extends Decorator{
    public function handle(Request $request){
        $request->str = $request->str . '查找方法';
        return $request;
    }
}

$request = new Request();

$request->handle();

$request = (new checkToken())->handle($request);

$request = (new trim())->handle($request);

$request = (new findMethod())->handle($request);

echo $request->str;
  • 闭包函数实现
<?php

//原始类
class Request{
    public $str;

    public function handle(){
            $this->str = '初始化的http请求!';
    }
}

//抽象装饰类
abstract class Decorator {
    public abstract function handle(Request $request,Closure $closures);
}

//验证 token 装饰类
class checkToken extends Decorator{
    public function handle(Request $request,Closure $closures){
        $request->str = $request->str . '验证 token ';
        return $closures($request);
    }
}

//过滤数据装饰器
class trim extends Decorator{
    public function handle(Request $request,Closure $closures){
        $request->str = $request->str . '过滤字符串';
        return $closures($request);
    }
}

//寻找方法装饰器
class findMethod extends Decorator{
    public function handle(Request $request,Closure $closures){
        $request->str = $request->str . '查找方法';
        return $closures($request);
    }
}

$request = new Request();
$request->handle();

$fun = function($request,$func,$decorator){
    return $decorator->handle($request,$func);
};

$func0 = function($request){
    return $request;
};

$decorator = new checkToken();
$func1 = function($request) use ($func0,$decorator,$fun){
    return $fun($request,$func0,$decorator);
};

$decorator = new trim();
$func2 = function($request) use ($func1,$decorator,$fun){
    return $fun($request,$func1,$decorator);
};

$decorator = new findMethod();
$func3 = function($request) use ($func2,$decorator,$fun){
    return $fun($request,$func2,$decorator);
};

$request = $func3($request);

var_dump($request->str);
  • 闭包装饰器抽象化
<?php

//原始类
class Request{
    public $str;

    public function handle(){
            $this->str = '初始化的http请求!';
    }
}

//抽象装饰类
abstract class Decorator {
    public abstract function handle(Request $request,Closure $closures);
}

//验证 token 装饰类
class checkToken extends Decorator{
    public function handle(Request $request,Closure $closures){
        $request->str = $request->str . '验证 token ';
        return $closures($request);
    }
}

//过滤数据装饰器
class trim extends Decorator{
    public function handle(Request $request,Closure $closures){
        $request->str = $request->str . '过滤字符串';
        return $closures($request);
    }
}

//寻找方法装饰器
class findMethod extends Decorator{
    public function handle(Request $request,Closure $closures){
        $request->str = $request->str . '查找方法';
        return $closures($request);
    }
}

$request = new Request();
$request->handle();

$fuc = function($fuc,$decorator){
    return function($request) use ($fuc,$decorator) {
        return $decorator->handle($request, $fuc); 
    };
};

$fuc0 = function($request) {
    return $request;
};

$fuc1 = $fuc($fuc0,(new checkToken()));

$fuc2 = $fuc($fuc1,(new trim()));

$fuc3 = $fuc($fuc2,(new findMethod()));

$request = $fuc3($request);

var_dump($request->str);
  • 自动化闭包装饰器
<?php

//原始类
class Request{
    public $str;

    public function handle(){
            $this->str = '初始化的http请求!';
    }
}

//抽象装饰类
abstract class Decorator {
    public abstract function handle(Request $request,Closure $closures);
}

//验证 token 装饰类
class checkToken extends Decorator{
    public function handle(Request $request,Closure $closures){
        $request->str = $request->str . '验证 token ';
        return $closures($request);
    }
}

//过滤数据装饰器
class trim extends Decorator{
    public function handle(Request $request,Closure $closures){
        $request->str = $request->str . '过滤字符串';
        return $closures($request);
    }
}

//寻找方法装饰器
class findMethod extends Decorator{
    public function handle(Request $request,Closure $closures){
        $request->str = $request->str . '查找方法';
        return $closures($request);
    }
}

$request = new Request();
$request->handle();

$fuc = function($fuc,$decorator){
    return function($request) use ($fuc,$decorator) {
        return $decorator->handle($request, $fuc); 
    };
};

$fuc0 = function($request) {
    return $request;
};
$func = array_reduce(
    [new checkToken,new trim(),new findMethod()],$fuc,$fuc0
    );

$request = $func($request);

var_dump($request->str);

Snail's Blog
请先登录后发表评论
  • 最新评论
  • 总共0条评论
  • 本博客使用免费开源的 laravel-bjyblog v5.5.1.3 -develop 搭建 © 2014-2018 www.snail-c.cn 版权所有 ICP证:蜀ICP备18023253号-1
  • 联系邮箱:459921737@qq.com