1.什么是装饰者模式
动态给对象增加功能,从一个对象的外部来给对象添加功能,相当于改变了对象的外观,比用继承的方式更加的灵活。
当使用装饰后,从外部系统的角度看,就不再是原来的那个对象了,而是使用一系列的装饰器装饰过后的对象。
2.结构
角色:
- Component:组件对象的抽象接口,可以给这些对象动态的增加职责/功能。
- ConcreteComponent:具体的组件的对象,实现组件对象的接口,是被装饰器装饰的原始对象,即可以给这个对象动态的添加职责。
- Decorator:所有装饰器的抽象父类,实现了组件对象的接口,并且持有一个组件对象(被装饰的对象)。
- ConcreteDecorator:具体的装饰器,具体实现向装饰对象添加功能。
普通示例
新建一个普通的蛋糕类:
public class Cake { public String getCakeMsg(){ return "我是一个8英寸的普通蛋糕"; } public BigDecimal getPrice(){ return new BigDecimal("68"); } }
这时候,我们需要给蛋糕加点芒果,那可以再新建一个类去继承普通Cake类,然后重写其中的方法:
public class CakeAddMango extends Cake { @Override public String getCakeMsg() { return super.getCakeMsg() + "+1个芒果"; } @Override public BigDecimal getPrice() { return super.getPrice().add(new BigDecimal("10")); } }
这时候,如果不仅仅加芒果,还要再加点葡萄,那么可以再写一个类,继承CakeAddMango,然后重写其中的方法。
import java.math.BigDecimal; public class CakeAddMangoAndGrape extends CakeAddMango { @Override public String getCakeMsg() { return super.getCakeMsg() + "+1个葡萄"; } @Override public BigDecimal getPrice() { return super.getPrice().add(new BigDecimal("5")); } }
写个测试类测一下:
public class TestCake { public static void main(String[] args) { //普通蛋糕 Cake cake = new Cake(); System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice()); //加芒果蛋糕 CakeAddMango cakeAddMango = new CakeAddMango(); System.out.println(cakeAddMango.getCakeMsg() + ",价格:" + cakeAddMango.getPrice()); //加芒果和葡萄蛋糕 CakeAddMangoAndGrape cakeAddMangoAndGrape = new CakeAddMangoAndGrape(); System.out.println(cakeAddMangoAndGrape.getCakeMsg() + ",价格:" + cakeAddMangoAndGrape.getPrice()); } }
输出如下结果:
我是一个8英寸的普通蛋糕,价格:68 我是一个8英寸的普通蛋糕+1个芒果,价格:78 我是一个8英寸的普通蛋糕+1个芒果+1个葡萄,价格:83
看起来挺好的,能实现,但是假如我们加2个芒果呢?或者是我要加2个普通呢,或者说芒果和葡萄要组合,数量不一定,那利用现有的类是无法实现的,只能不断加类去重写,如果业务变更频繁,修改起来会是致命的。
正因为普通的实现方法有这种缺陷,才有了装饰者模式,接下来我们来看看同一个需求利用装饰者模式是怎么实现的吧。
装饰者模式示例
1、新建一个蛋糕的抽象类:
import java.math.BigDecimal; public abstract class Cake { public abstract String getCakeMsg(); public abstract BigDecimal getPrice(); }
2、然后新建一个普通蛋糕的类:
import java.math.BigDecimal; public class BaseCake extends Cake { @Override public String getCakeMsg() { return "我是一个8英寸的普通蛋糕"; } @Override public BigDecimal getPrice() { return new BigDecimal("68"); } }
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 CakeAddGrapeDecorator extends CakeDecorator { public CakeAddGrapeDecorator(Cake cake) { super(cake); } @Override public String getCakeMsg() { return super.getCakeMsg() + "+1个葡萄"; } @Override public BigDecimal getPrice() { return super.getPrice().add(new BigDecimal("5")); } }
5、新建一个葡萄的装饰器类继承CakeDecorator类:
public class CakeAddMangoDecorator extends CakeDecorator { public CakeAddMangoDecorator(Cake cake) { super(cake); } @Override public String getCakeMsg() { return super.getCakeMsg() + "+1个芒果"; } @Override public BigDecimal getPrice() { return super.getPrice().add(new BigDecimal("10")); } }
6、最后写一个测试类测试一下:
public class TestCakeDecorator { public static void main(String[] args) { Cake cake = null; //普通蛋糕 cake = new BaseCake(); System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice()); //加一个芒果 cake = new CakeAddMangoDecorator(cake); System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice()); //加一个葡萄 cake = new CakeAddGrapeDecorator(cake); System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice()); //再加一个芒果 cake = new CakeAddMangoDecorator(cake); System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice()); } }
输出结果为:
我是一个8英寸的普通蛋糕,价格:68 我是一个8英寸的普通蛋糕+1个芒果,价格:78 我是一个8英寸的普通蛋糕+1个芒果+1个葡萄,价格:83 我是一个8英寸的普通蛋糕+1个芒果+1个葡萄+1个芒果,价格:93
我们可以看到,使用装饰者模式之后,扩展之前的功能变得极为方便,可以根据现有的装饰器进行任意组合。
类图关系
看一下类图,首先是一个基础抽象类定义了基本方法,然后是基础实现和基础装饰器继承并重写抽象类中的方法:
装饰者模式使用场景
-
1、用于扩展一个类的功能或给一个类添加附加职责。
-
2、动态的给一个对象添加功能,这些功能可以再动态的撤销。
装饰者模式优点
-
1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象 扩展功能,即插即用。
-
2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。
-
3、装饰者完全遵守开闭原则。
装饰者模式缺点
-
1、会出现更多的代码,更多的类,增加程序复杂性。
-
2、动态装饰以及多层装饰时会更加复杂。