什么是装饰器模式?
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
我们通过下面的实例来演示装饰器模式的用法。其中,我们将把一个形状装饰上不同的颜色,同时又不改变形状类。
普通示例
1.新建一个普通的蛋糕类
import java.math.BigDecimal;
public class Cake {
public String getCakeMsg() {
return "我是一个普通的蛋糕";
}
public BigDecimal getPrice() {
return new BigDecimal("58");
}
}
2.这个时候,我们需要给蛋糕加点芒果,那可以在再新建一个类去继承普通Cake类,然后重写其中的方法
import java.math.BigDecimal;
public class CakeAddMango extends Cake {
@Override
public String getCakeMsg() {
return super.getCakeMsg()+"+2个芒果";
}
@Override
public BigDecimal getPrice() {
return super.getPrice().add(new BigDecimal("10"));
}
}
3.这时候,如果不仅仅加芒果,还要在加点葡萄,那么可以在写一个类,继承CakeAddMango,让后重写其中的方法
import java.math.BigDecimal;
public class CakeAddMangoAddGrape extends CakeAddMango {
@Override
public String getCakeMsg() {
return super.getCakeMsg()+"+1个葡萄";
}
@Override
public BigDecimal getPrice() {
return super.getPrice().add(new BigDecimal("5"));
}
}
写个测试类测一下
@Test
public void test1() {
// 1.普通的蛋糕
Cake cake = new Cake();
System.out.println(cake.getCakeMsg() + " 价格: " + cake.getPrice());
// 2. 加芒果的蛋糕
CakeAddMango cakeAddMango = new CakeAddMango();
System.out.println(cakeAddMango.getCakeMsg()+" 价格: "+cakeAddMango.getPrice());
// 3.加芒果和葡萄的当高
CakeAddMangoAddGrape cakeAddMangoAddGrape = new CakeAddMangoAddGrape();
System.out.println(cakeAddMangoAddGrape.getCakeMsg()+" 价格: "+cakeAddMangoAddGrape.getPrice());
}
问题引入: 看起来挺好的,能实现,但是假如我们加4个芒果呢?或者我要加两个普通的葡萄呢?或者说芒果和葡萄要组合,数量不一定,那么利用现有的类时无法实现的,只能不断加类去重写,如果业务变的更麻烦,修改也是致命的
正因为普通的实现方法有这种缺陷,才有了装饰器模式,接下来我们用装饰器类实现上面的需求
装饰者模式示例
1.新建一个蛋糕的抽象类
package com.sth.spring.zsz;
import java.math.BigDecimal;
public abstract class Cake {
public abstract String getCakeMsg();
public abstract BigDecimal getPrice();
}
2.然后新建一个普通蛋糕的类
package com.sth.spring.zsz;
import java.math.BigDecimal;
public class BaseCake extends Cake {
@Override
public String getCakeMsg() {
return "我是一个普通的蛋糕";
}
@Override
public BigDecimal getPrice() {
return new BigDecimal("58");
}
}
3.新建一个蛋糕的装饰器类,内部持有蛋糕Cake对象,这个就是扩展的关键:
import java.math.BigDecimal;
public abstract class CakeDecorator extends Cake {
private Cake cake;
public CakeDecorator(Cake cake) {
this.cake = cake;
}
@Override
public String getCakeMsg() {
return this.cake.getCakeMsg();
}
@Override
public BigDecimal getPrice() {
return this.cake.getPrice();
}
}
4.新建一个芒果蛋糕的装饰器类继承CakeDecorator类:
import java.math.BigDecimal;
public class CakeAddMangoDecorator extends CakeDecorator {
public CakeAddMangoDecorator(Cake cake) {
super(cake);
}
@Override
public String getCakeMsg() {
return super.getCakeMsg() + "+2个芒果";
}
@Override
public BigDecimal getPrice() {
return super.getPrice().add(new BigDecimal("10"));
}
}
5.新建一个葡萄蛋糕的装饰器类继承CakeDecorator类:
import java.math.BigDecimal;
public class CakeAddGrapDecorator extends CakeDecorator {
public CakeAddGrapDecorator(Cake cake) {
super(cake);
}
@Override
public String getCakeMsg() {
return super.getCakeMsg() + "+1个葡萄";
}
@Override
public BigDecimal getPrice() {
return super.getPrice().add(new BigDecimal("5"));
}
}
6.最后写一个测试类测试一下
@Test
public void test1() {
Cake cake=null;
// 普通蛋糕
cake=new BaseCake();
System.out.println(cake.getCakeMsg()+"价格: "+cake.getPrice());
// 加2个芒果
cake= new CakeAddMangoDecorator(cake);
System.out.println(cake.getCakeMsg()+"价格: "+cake.getPrice());
// 加一个葡萄
cake = new CakeAddGrapDecorator(cake);
System.out.println(cake.getCakeMsg()+"价格: "+cake.getPrice());
// 再加2个芒果
cake= new CakeAddMangoDecorator(cake);
System.out.println(cake.getCakeMsg()+"价格: "+cake.getPrice());
}
我们现在可以看到,使用装饰器模式之后,扩展之前的功能变的极为方便,可以根据现有的装饰器进行任意组合
类关系图
看一下类图,首先是一个基础抽象类定义了基本的方法,然后是基础实现和基础装饰器继承并重写抽象类的方法:
装饰器模式的使用场景
- 用于扩展一个类的功能or給一个类添加附加职责
- 动态的该一个对象添加功能,这些功能可以动态的撤销
**注:**Mybatis中的二级缓存就是装饰器模式来进行动态扩展.
装饰器模式的优点
- 装饰器是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地給一个对象扩展功能,即插即用
- 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同的效果
- 装饰器完全遵守开闭原则
装饰器模式的缺点
- 会出现更多的代码,更多的类,增加程序的复杂性
- 动态装饰以及多层装饰时会更加复杂