定义
- 在不改变原有对象的基础之上,将功能附加到对象上
- 提供了不继承更有弹性的替代方案(扩展原有对象功能)
类型
- 结构型
使用场景
- 扩展一个类的功能或给一个类添加附加职责
- 动态的给一个对象添加功能,这功能可以再动态的撤销
优点
- 继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能
- 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同的效果
- 符合开闭原则
缺点
- 会出现更多的代码,更多的类,增加程序复杂性
- 动态装饰时,多层装饰时会更复杂
相关设计模式
- 装饰者模式和代理模式
装饰者模式关注一个对象动态的添加方法,代理模式在于控制对对象的访问,代理模式的代理类,可以对他的客户隐藏一个对线的具体信息,通常在使用代理模式时,在代理类中创建一个对象的实例,而使用装饰者模式时,通常会把原始对象做一个参数,传递给装饰者的构造器。
- 装饰者模式和适配器模式
两者都可以叫做包装模式,装饰者和被装饰者可以实现相同的接口,或者装饰者是被装饰者的子类。在适配器中,适配器和被适配的类具有不同的接口,有可能部分接口是重合的。
代码示例
首先我们假设一个场景。我们早上吃早饭。买个煎饼果子,可以加蛋加烤肠。并计算价格。按一般方法我们是这么写的
- 创建一个煎饼果子对象
public class Battercake {
protected String getDesc(){
return "煎饼果子";
}
protected int cost(){
return 8;
}
}
- 创建一个加蛋的煎饼果子
public class BattercakeWithEgg extends Battercake{
@Override
protected String getDesc() {
return super.getDesc() + "加一个鸡蛋";
}
@Override
protected int cost() {
return super.cost() + 2;
}
}
- 创建一个加蛋加烤肠的煎饼果子
public class BattercakeWithEggSausage extends BattercakeWithEgg{
@Override
protected String getDesc() {
return super.getDesc() + "加一根烤肠";
}
@Override
protected int cost() {
return super.cost() + 2;
}
}
- 我们现在可以得到三种类型的煎饼果子
public class Test {
public static void main(String[] args) {
Battercake battercake = new Battercake();
System.out.println(battercake.getDesc() + ",价格:" + battercake.cost());
BattercakeWithEgg battercakeWithEgg = new BattercakeWithEgg();
System.out.println(battercakeWithEgg.getDesc() + ",价格:" + battercakeWithEgg.cost());
BattercakeWithEggSausage battercakeWithEggSausage = new BattercakeWithEggSausage();
System.out.println(battercakeWithEggSausage.getDesc() + ",价格:" + battercakeWithEggSausage.cost());
}
}
执行结果
煎饼果子,价格:8
煎饼果子加一个鸡蛋,价格:10
煎饼果子加一个鸡蛋加一根烤肠,价格:12
UML
通过uml我们可以看出。这就是一个单纯的继承,它现在的扩展性是非常差的。假设我们的煎饼果子可以加很多不同的东西。那它的组合是非常复杂的。那按我们现在的这种写法,可能会发生类数量爆炸的情况。这是我们就要考虑让我们的代码更加优雅,如何实现呢,当然是我们的装饰者模式啦。
步骤
组成:抽象实体类,确定的实体类,抽象的装饰者,确定的装饰者
- 抽象实体类,我们要对煎饼进行抽象。
public abstract class AbstractBattercake {
protected abstract String getDesc();
protected abstract int cost();
}
- 确定的实体类,具体的煎饼
public class Battercake {
protected String getDesc(){
return "煎饼果子";
}
protected int cost(){
return 8;
}
}
- 抽象的装饰者
public abstract class AbstractDecorator extends AbstractBattercake{
private AbstractBattercake abstractBattercake;
//我们需要引入被修饰对象的实例变量,所以就作为构造参数输入了
public AbstractDecorator(AbstractBattercake abstractBattercake) {
this.abstractBattercake = abstractBattercake;
}
@Override
protected String getDesc() {
return this.abstractBattercake.getDesc();
}
@Override
protected int cost() {
return this.abstractBattercake.cost();
}
}
- 具体的装饰者
public class EggDecorator extends AbstractDecorator{
public EggDecorator(AbstractBattercake abstractBattercake) {
super(abstractBattercake);
}
@Override
protected String getDesc() {
return super.getDesc() + "加一个鸡蛋";
}
@Override
protected int cost() {
return super.cost() + 2;
}
}
public class SausageDecorator extends AbstractDecorator{
public SausageDecorator(AbstractBattercake abstractBattercake) {
super(abstractBattercake);
}
@Override
protected String getDesc() {
return super.getDesc() + "加一根烤肠";
}
@Override
protected int cost() {
return super.cost() + 2;
}
}
- 为了体现与开始写法不同。我们要获得一个 加两个鸡蛋一根烤肠的煎饼果子
public class Test {
public static void main(String[] args) {
AbstractBattercake abstractBattercake;
abstractBattercake = new Battercake();
abstractBattercake = new EggDecorator(abstractBattercake);
abstractBattercake = new EggDecorator(abstractBattercake);
abstractBattercake = new SausageDecorator(abstractBattercake);
System.out.println(abstractBattercake.getDesc() + ",价格:" + abstractBattercake.cost());
}
}
执行结果
煎饼果子加一个鸡蛋加一个鸡蛋加一根烤肠,价格:14
UML