装饰器模式
# 装饰器模式(Decorator Pattern)
装饰器模式也叫做包装模式,是指在不改变原有对象的基础上,将功能附加到对象上,提供比继承更有弹性的替代方案(扩展原有对象的功能),
强调一点是基于已有对象的功能增强;
装饰器模式属于结构性模式;
## 装饰器类图

### Component 抽象构件
> 定义一个抽象接口以规范准备接受附加责任的对象
```text
public abstract class Component {
public abstract void operation();
}
```
### ConcreteComponent 具体的构件
> 实现抽象构件,通过装饰角色为其添加一些职责
```text
public class ConcreteComponent extends Component{
@Override
public void operation() {
System.err.println("Do some biz event!");
}
}
```
### Decorator 抽象装饰角色
> 继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能
```text
public abstract class Decorator extends Component{
//持有组件对象
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
//转发请求给组件对象,可以在转发前后执行一些附加动作
component.operation();
}
}
```
### ConcreteDecoratorA
> 实现抽象装饰的相关方法,并给具体构件对象添加附加的责任
```text
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
public void operationFirst() {
System.err.println("ConcreteDecoratorA first!");
}
public void operationLast() {
System.err.println("ConcreteDecoratorA last!");
}
@Override
public void operation() {
operationFirst();
super.operation();
operationLast();
}
}
```
### Client 测试类
```text
public class Client {
public static void main(String[] args) {
Component component = new ConcreteComponent();
Decorator decoratorA = new ConcreteDecoratorA(component);
decoratorA.operation();
}
}
```
## 举个例子
买煎饼的时候,我们可以直接买一个煎饼,但有的时候觉得味道单一或者吃不饱,我们可以加一些东西,比如烤肠,鸡蛋,鸡排等等;
**先看看非装饰器模式的写法**
### BatterCake
> 就一个普通的煎饼类;
```text
public class BatterCake {
protected String getMsg(){return "煎饼";}
public int getPrice(){
return 10;
}
}
```
### BatterCakeWithEgg
> 添加一个鸡蛋的煎饼;
```text
public class BatterCakeWithEgg extends BatterCake{
protected String getMsg(){
return super.getMsg() + " 加一个鸡蛋";
}
public int getPrice(){
return super.getPrice() + 1;
}
}
```
### BatterCakeWithEggAndSauage
> 加一个鸡蛋 再加一跟烤肠的煎饼;
```text
public class BatterCakeWithEggAndSauage extends BatterCakeWithEgg{
protected String getMsg(){
return super.getMsg() + " 加一个烤肠";
}
public int getPrice(){
return super.getPrice() + 2;
}
}
```
### Client 客户
```text
public class Client {
public static void main(String[] args) {
BatterCake batterCake = new BatterCake();
System.err.println(batterCake.getMsg() + "价格 " + batterCake.getPrice());
BatterCakeWithEgg batterCakeWithEgg = new BatterCakeWithEgg();
System.err.println(batterCakeWithEgg.getMsg() + "价格 " + batterCakeWithEgg.getPrice());
BatterCakeWithEggAndSauage batterCakeWithEggAndSauage = new BatterCakeWithEggAndSauage();
System.err.println(batterCakeWithEggAndSauage.getMsg() + "价格 " + batterCakeWithEggAndSauage.getPrice());
}
}
```
看到这种写法,应该能看出它的弊端了,就是实现很简单,适合于需求固定的业务和多样性比较简单的业务;
一旦客户需要一个鸡蛋,两根烤肠,那是不是还要在BatterCakeWithEggAndSauage类上继续扩展呢,这其实是很low的设计;
而且非常不灵活,比如客户只需要一跟烤肠的煎饼。这个时候怎么解决呢;
**使用装饰器模式优化**
### BatterCake 抽象组件
```text
public abstract class BatterCake {
protected abstract String getMsg();
protected abstract int getPrice();
}
```
### 基础类 实现抽象接口
```text
public class BaseBatterCake extends BatterCake{
@Override
protected String getMsg() {
return "煎饼";
}
@Override
protected int getPrice() {
return 5;
}
}
```
### BatterCakeDecorator 装饰器
```text
public class BatterCakeDecorator extends BatterCake{
private BatterCake batterCake;
public BatterCakeDecorator(BatterCake batterCake) {
this.batterCake = batterCake;
}
@Override
protected String getMsg() {
return this.batterCake.getMsg();
}
@Override
protected int getPrice() {
return this.batterCake.getPrice();
}
}
```
### EggDecorator 具体的装饰器对象
```text
public class EggDecorator extends BatterCakeDecorator{
public EggDecorator(BatterCake batterCake) {
super(batterCake);
}
@Override
protected String getMsg() {
return super.getMsg() + " 加一个鸡蛋";
}
@Override
protected int getPrice() {
return super.getPrice() + 1;
}
}
```
### SauageDecorator 具体的装饰对象
```text
public class SauageDecorator extends BatterCakeDecorator{
public SauageDecorator(BatterCake batterCake) {
super(batterCake);
}
@Override
protected String getMsg() {
return super.getMsg() + " 加一个烤肠";
}
@Override
protected int getPrice() {
return super.getPrice() + 2;
}
}
```
### 客户 测试类
```text
public class Client {
public static void main(String[] args) {
BatterCake batterCake = new BaseBatterCake();
System.err.println(batterCake.getMsg() + "总计 : " + batterCake.getPrice());
batterCake = new EggDecorator(batterCake);
System.err.println(batterCake.getMsg() + "总计 : " + batterCake.getPrice());
batterCake = new SauageDecorator(batterCake);
System.err.println(batterCake.getMsg() + "总计 : " + batterCake.getPrice());
batterCake = new EggDecorator(batterCake);
System.err.println(batterCake.getMsg() + "总计 : " + batterCake.getPrice());
}
}
```
上面这种写法是运用了装饰器模式的写法,这样会增加程序的灵活性,EggDecorator返回一个BatterCake,在EggDecorator
中的getMsg方法和getPrice方法中添加关于Egg的逻辑来实现对BatterCake的增强,同理SauageDecorator也是,在它自己的getMsg方法和
getPrice方法中添加自己的逻辑,当然,都是基于调用super方法的基础上添加自己的逻辑,同时具体的装饰对象返回父类类型的对象;
```text
@Override
protected int getPrice() {
return super.getPrice() + 2;
}
```
**这里进行一下总结:**
1、具体装饰对象(EggDecorator)一定是继承自装饰组件(BatterCakeDecorator)
2、为了实现对象增强,子类中的方法一定是基于super方法的基础上,添加自己的逻辑的
## 使用场景
- 用于扩展一个类的功能或给一个类添加附加职责
- 动态地给一个对象添加功能,这些功能可以在动态的撤销
## 实际应用
Spring TransactionAwareCacheManager
JDK FileInputStream
## 装饰器模式与代理模式
- 装饰器模式是一种特殊的代理模式
- 装饰器模式强调自身的功能扩展,透明的,动态的扩展与增强
> 透明指的是功能的扩展由客户端控制
- 代理模式强调代理过程的控制
## 装饰器模式的优点
1、装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
2、通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
3、装饰器模式完全遵守开闭原则
## 装饰器模式的缺点
1、增加了一些子类,系统代码会显得臃肿。
2、组合方式容易出错,代码可读性比较差。
## 参考文档
1、[包装模式就是这么简单啦](https://juejin.cn/post/6844903603178569741)
2、[装饰器模式(装饰设计模式)详解](http://c.biancheng.net/view/1366.html)
3、[java中的装饰设计模式,浅谈与继承之间的区别](https://www.cnblogs.com/losedMemory/p/6246029.html)
4、[JDK IO中的适配器模式和装饰者模式](https://blog.csdn.net/lsgqjh/article/details/63254876)