装饰模式 - Decorator Pattern
定义
动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。这样可以给某个对象而不是整个类添加一些功能。
设计的原则和思想
- 解耦的是装饰者和被装饰者。
- 不变部分是被装饰者,变化部分是装饰者。
- 核心思想是对对象自身功能进行增强。
一句话概括设计模式
通过组合的方式,增强对象自身的功能,并且支持多个装饰器嵌套使用。
经典装饰者模式
结构中包含的角色
- Component(抽象构件)
- ConcreteComponent(具体构件)
- Decorator(抽象装饰类)
- ConcreteDecorator(具体装饰类)
最小可表达代码
// 抽象构件类 | |
abstract class Component | |
{ | |
public abstract function display(); | |
} | |
// 具体构件类 | |
class ConcreteComponent extends Component | |
{ | |
public function display() | |
{ | |
echo ' 具体构件 '; | |
} | |
} | |
// 抽象装饰类 | |
abstract class ComponentDecorator extends Component | |
{ | |
protected $component; | |
public function __construct(Component $component) | |
{ | |
$this->component = $component; | |
} | |
} | |
// 具体装饰类 | |
class ConcreteDecorator extends ComponentDecorator | |
{ | |
public function display() | |
{ | |
echo ' 装饰前的行为 '; | |
$this->component->display(); | |
echo ' 装饰后的行为 '; | |
} | |
} | |
$decorator = new ConcreteDecorator(new ConcreteComponent); | |
$decorator->display(); |
Laravel管道闭包方式的装饰者模式
结构中包含的角色
- MiddlewareManager (中间件管理类/修饰者管理类)
- Middleware (抽象中间件/抽象装饰者)
- ConcreteMiddleWare (具体中间件/具体装饰者)
- Controller (控制器/被装饰者)
- Request (请求类)
最小可表达代码
class MiddlewareManager | |
{ | |
// 请求 | |
protected $request; | |
// 中间件 middlewares | |
protected $middlewares = []; | |
// 设置请求 | |
public function send($request) | |
{ | |
$this->request = $request; | |
return $this; | |
} | |
// 设置中间件 | |
public function through($middlewares) | |
{ | |
$this->middlewares = is_array($middlewares) ? $middlewares : func_get_args(); | |
return $this; | |
} | |
// 输入需要执行的控制器,并且开始装饰控制器 | |
public function then(Closure $controllerClosure) | |
{ | |
$middlewareClosure = array_reduce( | |
array_reverse($this->middlewares), $this->carry(), $this->prepareDestination($controllerClosure) | |
); | |
// 很多人不太熟悉 array_reduce 的用法,这里附上 foreach 的写法,方便理解。 | |
// $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); | |
} | |
// 设置请求作为参数传给控制器 | |
protected function prepareDestination(Closure $controllerClosure) | |
{ | |
return function ($request) use ($controllerClosure) { | |
return $controllerClosure($request); | |
}; | |
} | |
// 执行多个中间件的方法 | |
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("前"); | |
$response = $controllerClosure($request); | |
var_dump("后"); | |
return $response; | |
} | |
} | |
class Controller | |
{ | |
public function test(Request $request) | |
{ | |
var_dump("执行控制器的方法"); | |
return ['status' => 200]; | |
} | |
} | |
$response = (new MiddlewareManager) | |
->send(new Request) | |
->through([new ConcreteMiddleWare]) | |
->then(function ($request) { | |
return (new Controller)->test($request); | |
}); | |
var_dump($response); |
优点
- 你可以在运行时添加或删除对象的功能。
- 你可以用多个装饰封装对象来组合几种行为。
- 装饰类和被装饰类可以独立发展,不会相互耦合。
- 通过组合来替代继承,使用对象之间的关联关系取代类之间的继承关系。
缺点
- 在封装器栈中删除指定装饰器比较困难。
- 实现行为不受装饰栈顺序影响的装饰比较困难。
- 比继承更容易出错,排错也比较困难,代码较为繁琐。
- 多层装饰比较复杂。
何时使用
- 扩展一个类的功能。
- 动态增加功能。
- 当不能采用继承的方式(final)对系统进行扩展或者采用继承不利于系统扩展和维护(大量独立扩展)
实际应用场景
- Laravel的中间件。
- 汉堡包,加番茄酱,加牛肉,加鸡蛋。。。
- Java的IO流。