Chain of Responsibility

7/15/2021 OOPPatternBehavioral pattern

# Description:

Chain of Responsibility is behavioral design pattern that allows passing request along the chain of potential handlers until one of them handles request.

  • Xây dựng 1 chuỗi quá trình xử lí cho đến khi 1 trong số đó xử lí được yêu cầu.

# Example:

Xây dựng chuỗi xử lí validate cho input.

interface RuleHandle
{
    public function addRule(RuleHandle $handler): RuleHandle;

    public function handle(string $attribute): bool;
}
1
2
3
4
5
6
  • addRule: Thêm next rule cho chain.
  • handle: Xử lí check validation cho rule.
abstract class AbstractRule implements RuleHandle
{
    /**
     * @var RuleHandle
     */
    private RuleHandle $nextHandler;

    public function addRule(RuleHandle $handler): RuleHandle
    {
        $this->nextHandler = $handler;
        return $handler;
    }

    public function handle(string $attribute): bool
    {
        if (isset($this->nextHandler)) {
            return $this->nextHandler->handle($attribute);
        }

        return true;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Rule extends AbstractRule
{
    public function handle($input): bool
    {
        return parent::handle($input);
    }
}
1
2
3
4
5
6
7
class NotEmptyRule extends AbstractRule
{
    public function handle($input): bool
    {
        if (empty($input)) {
            return false;
        } else {
            return parent::handle($input);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
class IsStringRule extends AbstractRule
{
    public function handle($input): bool
    {
        if (!is_string($input)) {
            return false;
        } else {
            return parent::handle($input);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
class StringLengthRule extends AbstractRule
{
    protected int $min;
    protected int $max;

    public function __construct($min, $max)
    {
        $this->min = $min;
        $this->max = $max;
    }

    public function handle($input): bool
    {
        if (strlen($input) < $this->min || strlen($input) > $this->max) {
            return false;
        } else {
            return parent::handle($input);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Validation
{
    protected RuleHandle $rule;

    public function setRule(RuleHandle $rule)
    {
        $this->rule = $rule;
        return $this;
    }

    public function check($input)
    {
        if ($this->rule->handle($input)) {
            echo "Pass";
        } else {
            echo "Fail";
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# Usage:

$foo = "abcdef";

$rule = new Rule();
$rule->addRule(new NotEmptyRule())
    ->addRule(new IsStringRule())
    ->addRule(new StringLengthRule(5, 8));

$validation = new Validation();
$validation->setRule($rule)
    ->check($foo);
1
2
3
4
5
6
7
8
9
10

Dump giá trị $rule

Nin\Validation\Rule {#65 ▼
  -nextHandler: Nin\Validation\NotEmptyRule {#64 ▼
    -nextHandler: Nin\Validation\IsStringRule {#62 ▼
      -nextHandler: Nin\Validation\StringLengthRule {#63 ▼
        #min: 5
        #max: 8
      }
    }
  }
}
1
2
3
4
5
6
7
8
9
10

Các rule sau khi addRule sẽ hình thành 1 chain, khi xử lí check từng rule sẽ call đến method handle(). Nếu fail rule đó chain sẽ dừng lại, còn pass sẽ tiếp tục call đến handle của rule tiếp theo.


Refer: https://refactoring.guru/design-patterns/chain-of-responsibility/php/example