代码改变世界

深入 Laravel 内核之装饰模式

2020-06-22 14:33  小伍2013  阅读(336)  评论(0编辑  收藏  举报

装饰模式核心内容:

  • 装饰模式可以在不影响组件对象的情况下,以动态、透明的方式从外部给对象添加功能;
  • 装饰器模式的本质就是动态组合。动态是手段,组合是目的。装饰模式是通过把复杂的功能简单化、分散化,在运行期间,根据需要来动态组合出需要的功能;
  • 使用不同的装饰类以及这些装饰类的排列组合,可以创造出很多不同功能的对象。可以使用多个装饰类来装饰同一对象,创造功能更为强大的对象;
  • 装饰抽象类的接口必须与组件抽象类的接口相同,对于客户端来说无论是装饰之前的对象还是装饰之后的对象都可以使用同样的方式调用;
  • 组件实现类 ConcreteComponent 应该尽量保持简洁和轻量,不要把主逻辑之外的辅助逻辑和状态放在组件实现类中,应该通过装饰类对其进行扩展。

定义一个组件抽象类:

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

定义一个组件实现类,实现组件功能:

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

定义一个装饰器抽象类,接口与组件类一致:

abstract class Decorator extends Component
{
    /**
     * 持有Component对象,该对象就是被装饰的对象
     */
    protected $component;

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

    abstract public function operation();
}

定义两个装饰器,用来扩展原有组件的功能,或者实现新的功能:

class ConcreteDecoratorA extends Decorator
{
    public function beforeOperation()
    {
        echo __CLASS__ . '|' . __METHOD__ . "\r\n";
    }

    public function afterOperation()
    {
        echo __CLASS__ . '|' . __METHOD__ . "\r\n";
    }

    public function operation()
    {
        $this->beforeOperation();
        //这里可以调用父类的方法,也可以不调用直接改写实现新功能
        $this->component->operation();
        $this->afterOperation();
    }
}

class ConcreteDecoratorB extends Decorator
{
    public function beforeOperation()
    {
        echo __CLASS__ . '|' . __METHOD__ . "\r\n";
    }

    public function afterOperation()
    {
        echo __CLASS__ . '|' . __METHOD__ . "\r\n";
    }

    public function operation()
    {
        $this->beforeOperation();
        //这里可以调用父类的方法,也可以不调用直接改写实现新功能
        $this->component->operation();
        $this->afterOperation();
    }
}

客户端使用示例:

class Client
{
    public function main()
    {
        $component = new ConcreteComponent();
        echo "\r\n直接使用组件:\r\n";
        $component->operation();
        
        $decoratorA = new ConcreteDecoratorA($component);
        echo "\r\n使用装饰器A装饰后的组件:\r\n";
        $decoratorA->operation();
        
        $decoratorB = new ConcreteDecoratorB($decoratorA);
        echo "\r\n使用装饰器A和B装饰后的组件:\r\n";
        $decoratorB->operation();
    }
}

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