TungNT (Blue)

tungnt.blue@gmail.com

User Tools

Site Tools


development:software-architecture:design-patterns:chain-of-responsibility

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
development:software-architecture:design-patterns:chain-of-responsibility [2024/08/06 06:59] – created - external edit 127.0.0.1development:software-architecture:design-patterns:chain-of-responsibility [2024/08/24 03:28] (current) tungnt
Line 1: Line 1:
 ====== Chain of Responsibility ====== ====== Chain of Responsibility ======
  
 +https://refactoring.guru/design-patterns/chain-of-responsibility
  
-====== Tham khảo ======+{{ :development:software-architecture:design-patterns:chain-of-responsibility.png |}}
  
-  * https://refactoring.guru/design-patterns/chain-of-responsibility+{{ :development:software-architecture:design-patterns:solution1-en.png |}}
  
 +**Ví dụ 1:**
 +
 +<file php>
 +<?php
 +
 +namespace RefactoringGuru\ChainOfResponsibility\Conceptual;
 +
 +/**
 + * The Handler interface declares a method for building the chain of handlers.
 + * It also declares a method for executing a request.
 + */
 +interface Handler
 +{
 +    public function setNext(Handler $handler): Handler;
 +
 +    public function handle(string $request): ?string;
 +}
 +
 +/**
 + * The default chaining behavior can be implemented inside a base handler class.
 + */
 +abstract class AbstractHandler implements Handler
 +{
 +    /**
 +     * @var Handler
 +     */
 +    private $nextHandler;
 +
 +    public function setNext(Handler $handler): Handler
 +    {
 +        $this->nextHandler = $handler;
 +        // Returning a handler from here will let us link handlers in a
 +        // convenient way like this:
 +        // $monkey->setNext($squirrel)->setNext($dog);
 +        return $handler;
 +    }
 +
 +    public function handle(string $request): ?string
 +    {
 +        if ($this->nextHandler) {
 +            return $this->nextHandler->handle($request);
 +        }
 +
 +        return null;
 +    }
 +}
 +
 +/**
 + * All Concrete Handlers either handle a request or pass it to the next handler
 + * in the chain.
 + */
 +class MonkeyHandler extends AbstractHandler
 +{
 +    public function handle(string $request): ?string
 +    {
 +        if ($request === "Banana") {
 +            return "Monkey: I'll eat the " . $request . ".\n";
 +        } else {
 +            return parent::handle($request);
 +        }
 +    }
 +}
 +
 +class SquirrelHandler extends AbstractHandler
 +{
 +    public function handle(string $request): ?string
 +    {
 +        if ($request === "Nut") {
 +            return "Squirrel: I'll eat the " . $request . ".\n";
 +        } else {
 +            return parent::handle($request);
 +        }
 +    }
 +}
 +
 +class DogHandler extends AbstractHandler
 +{
 +    public function handle(string $request): ?string
 +    {
 +        if ($request === "MeatBall") {
 +            return "Dog: I'll eat the " . $request . ".\n";
 +        } else {
 +            return parent::handle($request);
 +        }
 +    }
 +}
 +
 +/**
 + * The client code is usually suited to work with a single handler. In most
 + * cases, it is not even aware that the handler is part of a chain.
 + */
 +function clientCode(Handler $handler)
 +{
 +    foreach (["Nut", "Banana", "Cup of coffee"] as $food) {
 +        echo "Client: Who wants a " . $food . "?\n";
 +        $result = $handler->handle($food);
 +        if ($result) {
 +            echo "  " . $result;
 +        } else {
 +            echo "  " . $food . " was left untouched.\n";
 +        }
 +    }
 +}
 +
 +/**
 + * The other part of the client code constructs the actual chain.
 + */
 +$monkey = new MonkeyHandler();
 +$squirrel = new SquirrelHandler();
 +$dog = new DogHandler();
 +
 +$monkey->setNext($squirrel)->setNext($dog);
 +
 +/**
 + * The client should be able to send a request to any handler, not just the
 + * first one in the chain.
 + */
 +echo "Chain: Monkey > Squirrel > Dog\n\n";
 +clientCode($monkey);
 +echo "\n";
 +
 +echo "Subchain: Squirrel > Dog\n\n";
 +clientCode($squirrel);
 +</file>
 +
 +**Ví dụ 2:**
 +
 +<file php>
 +<?php
 +
 +namespace RefactoringGuru\ChainOfResponsibility\RealWorld;
 +
 +/**
 + * The classic CoR pattern declares a single role for objects that make up a
 + * chain, which is a Handler. In our example, let's differentiate between
 + * middleware and a final application's handler, which is executed when a
 + * request gets through all the middleware objects.
 + *
 + * The base Middleware class declares an interface for linking middleware
 + * objects into a chain.
 + */
 +abstract class Middleware
 +{
 +    /**
 +     * @var Middleware
 +     */
 +    private $next;
 +
 +    /**
 +     * This method can be used to build a chain of middleware objects.
 +     */
 +    public function linkWith(Middleware $next): Middleware
 +    {
 +        $this->next = $next;
 +
 +        return $next;
 +    }
 +
 +    /**
 +     * Subclasses must override this method to provide their own checks. A
 +     * subclass can fall back to the parent implementation if it can't process a
 +     * request.
 +     */
 +    public function check(string $email, string $password): bool
 +    {
 +        if (!$this->next) {
 +            return true;
 +        }
 +
 +        return $this->next->check($email, $password);
 +    }
 +}
 +
 +/**
 + * This Concrete Middleware checks whether a user with given credentials exists.
 + */
 +class UserExistsMiddleware extends Middleware
 +{
 +    private $server;
 +
 +    public function __construct(Server $server)
 +    {
 +        $this->server = $server;
 +    }
 +
 +    public function check(string $email, string $password): bool
 +    {
 +        if (!$this->server->hasEmail($email)) {
 +            echo "UserExistsMiddleware: This email is not registered!\n";
 +
 +            return false;
 +        }
 +
 +        if (!$this->server->isValidPassword($email, $password)) {
 +            echo "UserExistsMiddleware: Wrong password!\n";
 +
 +            return false;
 +        }
 +
 +        return parent::check($email, $password);
 +    }
 +}
 +
 +/**
 + * This Concrete Middleware checks whether a user associated with the request
 + * has sufficient permissions.
 + */
 +class RoleCheckMiddleware extends Middleware
 +{
 +    public function check(string $email, string $password): bool
 +    {
 +        if ($email === "admin@example.com") {
 +            echo "RoleCheckMiddleware: Hello, admin!\n";
 +
 +            return true;
 +        }
 +        echo "RoleCheckMiddleware: Hello, user!\n";
 +
 +        return parent::check($email, $password);
 +    }
 +}
 +
 +/**
 + * This Concrete Middleware checks whether there are too many failed login
 + * requests.
 + */
 +class ThrottlingMiddleware extends Middleware
 +{
 +    private $requestPerMinute;
 +
 +    private $request;
 +
 +    private $currentTime;
 +
 +    public function __construct(int $requestPerMinute)
 +    {
 +        $this->requestPerMinute = $requestPerMinute;
 +        $this->currentTime = time();
 +    }
 +
 +    /**
 +     * Please, note that the parent::check call can be inserted both at the
 +     * beginning of this method and at the end.
 +     *
 +     * This gives much more flexibility than a simple loop over all middleware
 +     * objects. For instance, a middleware can change the order of checks by
 +     * running its check after all the others.
 +     */
 +    public function check(string $email, string $password): bool
 +    {
 +        if (time() > $this->currentTime + 60) {
 +            $this->request = 0;
 +            $this->currentTime = time();
 +        }
 +
 +        $this->request++;
 +
 +        if ($this->request > $this->requestPerMinute) {
 +            echo "ThrottlingMiddleware: Request limit exceeded!\n";
 +            die();
 +        }
 +
 +        return parent::check($email, $password);
 +    }
 +}
 +
 +/**
 + * This is an application's class that acts as a real handler. The Server class
 + * uses the CoR pattern to execute a set of various authentication middleware
 + * before launching some business logic associated with a request.
 + */
 +class Server
 +{
 +    private $users = [];
 +
 +    /**
 +     * @var Middleware
 +     */
 +    private $middleware;
 +
 +    /**
 +     * The client can configure the server with a chain of middleware objects.
 +     */
 +    public function setMiddleware(Middleware $middleware): void
 +    {
 +        $this->middleware = $middleware;
 +    }
 +
 +    /**
 +     * The server gets the email and password from the client and sends the
 +     * authorization request to the middleware.
 +     */
 +    public function logIn(string $email, string $password): bool
 +    {
 +        if ($this->middleware->check($email, $password)) {
 +            echo "Server: Authorization has been successful!\n";
 +
 +            // Do something useful for authorized users.
 +
 +            return true;
 +        }
 +
 +        return false;
 +    }
 +
 +    public function register(string $email, string $password): void
 +    {
 +        $this->users[$email] = $password;
 +    }
 +
 +    public function hasEmail(string $email): bool
 +    {
 +        return isset($this->users[$email]);
 +    }
 +
 +    public function isValidPassword(string $email, string $password): bool
 +    {
 +        return $this->users[$email] === $password;
 +    }
 +}
 +
 +/**
 + * The client code.
 + */
 +$server = new Server();
 +$server->register("admin@example.com", "admin_pass");
 +$server->register("user@example.com", "user_pass");
 +
 +// All middleware are chained. The client can build various configurations of
 +// chains depending on its needs.
 +$middleware = new ThrottlingMiddleware(2);
 +$middleware
 +    ->linkWith(new UserExistsMiddleware($server))
 +    ->linkWith(new RoleCheckMiddleware());
 +
 +// The server gets a chain from the client code.
 +$server->setMiddleware($middleware);
 +
 +// ...
 +
 +do {
 +    echo "\nEnter your email:\n";
 +    $email = readline();
 +    echo "Enter your password:\n";
 +    $password = readline();
 +    $success = $server->logIn($email, $password);
 +} while (!$success);
 +</file>
development/software-architecture/design-patterns/chain-of-responsibility.1722927590.txt.gz · Last modified: by 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki