Laravel内核分析-设计模式-装饰模式

装饰模式

我们在了解装饰模式之前,先回顾下生活中的几个常见现象,举例如下:

  • 新房的装修,房屋装修并没有改变房屋居住的本质,但可以让房屋变得更漂亮,更温馨,更实用,更满足居家需求。
  • 相片的包装,照相馆中把原相片清洗出来后,会对上面做些包装/装饰,相片镀膜,添加相框等处理,让整体更加美观,防潮保存更长的时间。
在软件设计中,类似上面的场景我们也可以把对象在不改变结构的情况下对其加工扩展修饰,使得对象具有更加强大的功能,这种技术在设计模式中就叫装饰模式。装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为。

往专业一点来说,装饰模式能够实现动态的为对象添加功能,是从一个对象外部来给对象添加功能。通常有两种方式可以实现给一个类或对象增加行为

  • 继承机制,使用继承机制是给现有类添加功能的一种有效途径,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。
  • 组合机制,即将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为,我们称这个嵌入的对象为装饰器 (Decorator)

装饰模式的优点

  • 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
  • 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
  • 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。

模式结构和说明

 

Component:组件对象的接口,可以给这些对象动态的添加职责;

ConcreteComponent:具体的组件对象,实现了组件接口。该对象通常就是被装饰器装饰的原始对象,可以给这个对象添加职责;

Decorator:所有装饰器的父类,需要定义一个与 Component 接口一致的接口 (主要是为了实现装饰器功能的复用,即具体的装饰器 A 可以装饰另外一个具体的装饰器 B,因为装饰器类也是一个 Component),并持有一个 Component 对象,该对象其实就是被装饰的对象。如果不继承 Component 接口类,则只能为某个组件添加单一的功能,即装饰器对象不能再装饰其他的装饰器对象。

ConcreteDecorator:具体的装饰器类,实现具体要向被装饰对象添加的功能。用来装饰具体的组件对象或者另外一个具体的装饰器对象。

 

示例代码

1.Component 抽象类,可以给这些对象动态的添加职责

abstract class Component
{
    abstract public function operation();
}

 

2.Component 的实现类

class ConcreteComponent extends Component
{
    public function operation()
    {
        echo __CLASS__ .  '|' . __METHOD__ . "\r\n";
    }
}

 

3.装饰器的抽象类

维持一个指向组件对象的接口对象, 并定义一个与组件接口一致的接口

abstract class Decorator extends Component
{
    /**
     * 持有Component的对象
     */
    protected $component;

    /**
     * 构造方法传入
     */
    public function __construct(Component $component)
    {
        $this->component = $component;
    }

    abstract public function operation();
}

4.装饰器的具体实现类

向组件对象添加职责,beforeOperation (),afterOperation () 为前后添加的职责

class ConcreteDecoratorA extends Decorator
{
    //在调用父类的operation方法的前置操作
    public function beforeOperation()
    {
        echo __CLASS__ . '|' . __METHOD__ . "\r\n";
    }

    //在调用父类的operation方法的后置操作
    public function afterOperation()
    {
        echo __CLASS__ . '|' . __METHOD__ . "\r\n";
    }

    public function operation()
    {
        $this->beforeOperation();
        $this->component->operation();//这里可以选择性的调用父类的方法,如果不调用则相当于完全改写了方法,实现了新的功能
        $this->afterOperation();
    }
}

class ConcreteDecoratorB extends Decorator
{
    //在调用父类的operation方法的前置操作
    public function beforeOperation()
    {
        echo __CLASS__ . '|' . __METHOD__ . "\r\n";
    }

    //在调用父类的operation方法的后置操作
    public function afterOperation()
    {
        echo __CLASS__ . '|' . __METHOD__ . "\r\n";
    }

    public function operation()
    {
        $this->beforeOperation();
        $this->component->operation();//这里可以选择性的调用父类的方法,如果不调用则相当于完全改写了方法,实现了新的功能
        $this->afterOperation();
    }
}

 

5.客户端使用装饰器

class Client
{
    public function main()
    {
        $component = new ConcreteComponent();
        $decoratorA = new ConcreteDecoratorA($component);
        $decoratorB = new ConcreteDecoratorB($decoratorA);
        $decoratorB->operation();
    }
}

$client = new Client();
$client->main();

装饰模式需要注意的问题

  • 一个装饰类的接口必须与被装饰类的接口保持相同,对于客户端来说无论是装饰之前的对象还是装饰之后的对象都可以一致对待。
  • 尽量保持具体组件类 ConcreteComponent 的轻量,不要把主逻辑之外的辅助逻辑和状态放在具体组件类中,可以通过装饰类对其进行扩展。 如果只有一个具体组件类而没有抽象组件类,那么抽象装饰类可以作为具体组件类的直接子类。

 

ps:参考设计模式--装饰模式 | 必备基础 |《Laravel内核分析》| Laravel China 社区 (learnku.com)

版权声明:
作者:linrux
链接:https://www.tot7.cn/technology/laravel/432.html
来源:阿信博客
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>
文章目录
关闭
目 录