Minimalist design mode - decoration mode

Posted by tharagleb on Sat, 04 Dec 2021 22:04:44 +0100

Decoration pattern - Decorator Pattern

definition

Dynamically add some additional responsibilities to an object. In terms of increasing object function, decoration mode is more flexible than generating subclass implementation. This adds some functionality to an object rather than to an entire class.

Principles and ideas of design

  1. Decouple the decorator and the decorated.
  2. The unchanged part is the decorator, and the changed part is the decorator.
  3. The core idea is to enhance the function of the object itself.

Summarize the design pattern in one sentence

Through combination, the function of the object itself is enhanced, and multiple decorators can be nested.

Classic decorator mode

Roles contained in structure

  1. Component (abstract component)
  2. ConcreteComponent (concrete component)
  3. Decorator (abstract decoration class)
  4. ConcreteDecorator (specific decoration)

Minimum expressible code

// Abstract component class
abstract class Component
{
    public abstract function display();
}

// Specific component class
class ConcreteComponent extends Component
{
    public function display()
    {
        echo ' Specific components ';
    }
}

// Abstract decoration class
abstract class ComponentDecorator extends Component
{
    protected $component;

    public function __construct(Component $component)
    {  
        $this->component = $component;
    }
}

// Specific decoration
class ConcreteDecorator extends  ComponentDecorator
{
    public function display()
    {
        echo ' Behavior before decoration ';
        $this->component->display();
        echo ' Behavior after decoration ';
    }
}

$decorator = new ConcreteDecorator(new ConcreteComponent);
$decorator->display();

Decorator mode of Laravel pipeline closure

Roles contained in structure

  1. MiddlewareManager (middleware management class / decorator management class)
  2. Middleware (Abstract Middleware / abstract decorator)
  3. ConcreteMiddleWare (specific Middleware / specific decorator)
  4. Controller (Controller / decorated)
  5. Request (request class)

Minimum expressible code

class MiddlewareManager
{
    // request
    protected $request;

    // Middleware Middleware
    protected $middlewares = [];

    // Set request
    public function send($request)
    {
        $this->request = $request;

        return $this;
    }

    // Set up Middleware
    public function through($middlewares)
    {
        $this->middlewares = is_array($middlewares) ? $middlewares : func_get_args();

        return $this;
    }

    // Enter the controller to be executed and start decorating the controller
    public function then(Closure $controllerClosure)
    {
        $middlewareClosure = array_reduce(
            array_reverse($this->middlewares), $this->carry(), $this->prepareDestination($controllerClosure)
        );

        // Many people are not familiar with array_ For the usage of reduce, the writing method of foreach is attached here for easy understanding.
        // $middlewareClosure = $this->prepareDestination($controllerClosure);
        // foreach (array_reverse($this->middlewares) as $middleware) {
        //     $middlewareClosure = function ($request) use ($middlewareClosure, $middleware) {
        //         return $middleware->handle($request, $middlewareClosure);
        //     };
        // }

        return $middlewareClosure($this->request);
    }

    // The setting request is passed to the controller as a parameter
    protected function prepareDestination(Closure $controllerClosure)
    {
        return function ($request) use ($controllerClosure) {
            return $controllerClosure($request);
        };
    }

    // Method of executing multiple Middleware
    protected function carry()
    {
        return function ($controllerClosure, $middleware) {
            return function ($request) use ($controllerClosure, $middleware) {
                return $middleware->handle($request, $controllerClosure);
            };
        };
    }
}

class Request{}

interface Middleware
{
    public function handle(Request $request, Closure $controllerClosure);
}

class ConcreteMiddleWare implements Middleware
{
    public function handle(Request $request, Closure $controllerClosure)
    {
        var_dump("front");

        $response = $controllerClosure($request);

        var_dump("after");

        return $response;
    }
}

class Controller
{
    public function test(Request $request)
    {
        var_dump("Method of executing controller");

        return ['status' => 200];
    }
}

$response = (new MiddlewareManager)
    ->send(new Request)
    ->through([new ConcreteMiddleWare])
    ->then(function ($request) {
        return (new Controller)->test($request);
    });

var_dump($response);

advantage

  1. You can add or remove objects at run time.
  2. You can combine several behaviors by encapsulating objects with multiple decorations.
  3. Decorative classes and decorated classes can develop independently without coupling each other.
  4. The inheritance is replaced by composition, and the inheritance relationship between classes is replaced by the association relationship between objects.

shortcoming

  1. It is difficult to remove the specified decorator from the wrapper stack.
  2. It is difficult to implement decoration whose behavior is not affected by the order of decoration stack.
  3. It is more error prone than inheritance, more difficult to debug, and more cumbersome code.
  4. Multi layer decoration is more complex.

When to use

  1. Extend the function of a class.
  2. Dynamic addition function.
  3. When the system cannot be extended by inheritance (final), or inheritance is not conducive to system expansion and maintenance (a large number of independent extensions)

Practical application scenario

  1. Laravel middleware.
  2. Hamburger, tomato sauce, beef, eggs...
  3. Java IO stream.

Topics: Laravel