在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
装饰器模式角色
Component
定义一个对象接口,可以给这些对象动态地添加职责。
ConcreteComponent
是定义了一个具体的对象,也可以给这个对象添加一些职责。
Decorator
装饰抽象类,继承了Component,扩展了Component类的功能,对于Component无需知道Decorator。
ConcreteDecorator
具体实现的装饰对象,给Component添加职责的功能。
适配器模式与装饰器模式的区别
装饰器与适配器都有一个别名叫做包装模式(Wrapper)(适配器模式包装是为了接口兼容性,装饰器模式包装是为了动态的添加方法或者是行为),它们都是起到包装一个类或对象的作用,但是使用时,它们的目的不一样。
适配器只需要将原接口转化为客户希望的另一个接口,就是适配器模式。转化无非就是一下2点。
1.继承原类或者实现原接口
2.持有原接口的对象,再实现目标接口。
在装饰器模式中,必须要有被装饰的类和装饰的类。装饰模式的一定是从外部传入,并且可以没有顺序,按照代码的实际需求随意挑换顺序。 装饰模式注重的是功能的拓展,在同一个方法下实现更多的功能。
代码情景
小明和小白和小农去吃手抓饼,小明点了一个普通手抓饼、小白点了一个加蛋的手抓饼,小农点了一个奢华版的手抓饼(加单加牛肉)接下来我们用代码来实现吧。
先不用装饰器模式进行实现。
蛋糕类
public interface Cake {
// 名字
public String nameDetails();
// 价格
public double price();
}
具体实现类(手抓饼类、加蛋手抓饼、加牛肉手抓饼、加蛋加牛肉手抓饼)
public class CakeImpl implements Cake {
@Override
public String nameDetails() {
return "普通版本手抓饼";
}
@Override
public double price() {
return 5.0;
}
}
public class EggCakeImpl implements Cake {
@Override
public String nameDetails() {
return "加蛋手抓饼";
}
@Override
public double price() {
return 6.0;
}
}
public class BeefCakeImpl implements Cake {
@Override
public String nameDetails() {
return "加牛肉手抓饼";
}
@Override
public double price() {
return 7.0;
}
}
public class BeefAndEggCakeImpl implements Cake {
@Override
public String nameDetails() {
return "加蛋加牛肉的手抓饼";
}
@Override
public double price() {
return 8.0;
}
}
测试类
public static void main(String [] args) {
Cake cake = new CakeImpl();
System.out.println("小明点了一份:"+cake.nameDetails()+"价格:"+cake.price());
Cake cake1 = new EggCakeImpl();
System.out.println("小白点了一份:"+cake1.nameDetails()+"价格:"+cake1.price());
Cake cake2 = new BeefAndEggCakeImpl();
System.out.println("小农点了一份:"+cake2.nameDetails()+"价格:"+cake2.price());
}
测试结果
小明点了一份:普通版本手抓饼价格:5.0
小白点了一份:加蛋手抓饼价格:6.0
小农点了一份:加蛋加牛肉的手抓饼价格:8.0
通过业务我们可以想到会有多种形式的手抓饼的类型,手抓饼我们可以看成对象,对象有多种组合,比如手抓饼可能有十几种原材料,如果有多种组合方式,我们就需要多种类。目前3种原材料就需要多种类进行实现,这样做是可以实现的,但是不符合设计模式编程,接下来我们用装饰着模式进行实现。
抽象对象类
public interface Cake {
// 名字
public String nameDetails();
// 价格
public double price();
}
基础的对象类
public class CakeImpl implements Cake {
@Override
public String nameDetails() {
return "手抓饼";
}
@Override
public double price() {
return 5.0;
}
}
装饰者抽象类
public abstract class CakeDecorator implements Cake {
Cake cake;
public CakeDecorator (Cake cake) {
this.cake = cake;
}
@Override
public String nameDetails() {
return cake.nameDetails();
}
@Override
public double price() {
return cake.price();
}
}
具体的装饰者类
public class EggCakeImpl extends CakeDecorator {
public EggCakeImpl(Cake cake) {
super(cake);
}
@Override
public String nameDetails() {
return "鸡蛋"+cake.nameDetails();
}
@Override
public double price() {
return cake.price()+1.5;
}
}
public class BeefCakeImpl extends CakeDecorator {
public BeefCakeImpl(Cake cake) {
super(cake);
}
@Override
public String nameDetails() {
return "牛肉"+cake.nameDetails();
}
@Override
public double price() {
return cake.price()+2.0;
}
}
测试类:
public static void main(String [] args) {
Cake cake = new CakeImpl();
System.out.println("小明点了一份:"+cake.nameDetails() +"价格:"+cake.price());
Cake cake1 = new EggCakeImpl(cake);
System.out.println("小白点了一份:"+cake1.nameDetails() +"价格:"+cake1.price());
// 标识了制作顺序与流程,再生产中流水线制作程序时,可能会比较严格的把控,制作流程。
// 装饰者模式不仅仅完成操作细节的流程,提供很好的调用方式。
Cake cake3 = new BeefCakeImpl(cake1);
System.out.println("小农点了一份:"+cake3.nameDetails() +"价格:"+cake3.price());
}
测试结果:
小明点了一份:手抓饼价格:5.0
小白点了一份:鸡蛋手抓饼价格:6.0
小农点了一份:牛肉鸡蛋手抓饼价格:8.0
装饰器模式的优缺点
优点
1、装饰者模式可以提供比继承更多的灵活性
2、可以通过一种动态的方式来扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为。
3、通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
4、具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。
缺点
1、会产生很多的小对象,增加了系统的复杂性 (你要考虑到,已经节省了很多类了)
2、这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。
应用场景
1、在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
2、需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
在jdk中的应用
java.io.BufferedInputStream(InputStream)
java.io.DataInputStream(InputStream)
java.io.BufferedOutputStream(OutputStream)
java.util.zip.ZipOutputStream(OutputStream)
java.util.Collections#checkedList|Map|Set|SortedSet|SortedMap
本文的代码GitHub地址:
https://github.com/xiaonongOne/decorator-test