先给自己定个小目标,因为之前一直对设计模式不是特别熟悉,用起来总是感觉很生疏。因此,从今天开始,将会进行为期一个月左右的设计模式的学习和整理,借此增加对设计模式的了解和应用能力。设计模式,归根结底,还是一种代码设计的思想和代码设计的风格,设计模式的使用,会对代码进行精简,使用设计模式也更容易实现代码的复用,更容易让其他人快速了解。下面可以开始我们今天的主角——装饰者模式。
装饰者模式:
顾名思义,装饰者模式,就是在原有的基类的基础上,给对象添加一些额外的职能,对对象进行包装和修饰,以此来展示出一个更为复杂的、功能更为齐全的新对象。装饰者模式跟子类继承的方式非常类似,都是在基类的基础上拓展功能,但是比基类更加的灵活,也符合多组合、少继承的设计原则,避免对象之间的耦合。
装饰着模式中的各个角色:
Component 抽象构件
它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理没有被装饰的对象,以及装饰后的对象,可以做到对客户端透明。
ConcreteComponent 具体构件
Component是我们对具体的类的一个向上抽象,具体构件就是我们实际的实现类了,实现抽象构件的抽象方法,实现最基本的描述该对象功能。
Decorator 抽象装饰类
抽象装饰类,是抽象构件的子类,用于给具体构件增加装饰的职能,具体增加什么功能一般是在具体装饰类里面实现,这个抽象装饰类,实际上就是一个中转,维护了一个可以指向抽象构件的引用,通过该引用可以直接调用具体构件的方法,已达到装饰的目的。
ConcreteDecorator 具体装饰类
具体装饰类,是抽象装饰类的实现,对抽象装饰类中的抽象方法,做具体的修饰。
装饰者模式的核心思想,就是对具体类和装饰类进行抽象,设计好的抽象装饰类,是装饰者模式的关键所在。

下面我们以一个生活中常见的例子,来看下装饰者模式咋用。网上基本上都是喝咖啡的例子,加奶加糖什么的,我比较喜欢吃的,举个吃鸡排的例子。
吃鸡排的时候,有各种口味的,原味的、椒盐味、孜然味、麻辣味等等各种口味,而且还可以带各种喝的,比如说带一杯酸梅汤等等。
// 抽象构件,形容鸡排的口味和价格
public abstract class AbstractChickenCutlet {
public abstract String desc();
public abstract double cost();
}
// 具体构件,对抽象构件的实现,包含我们默认的口味,是原味的,价格是10元
public class ChickenCutlet extends AbstractChickenCutlet {
@Override
public String desc() {
return "原味鸡排";
}
@Override
public double cost() {
return 10.0f;
}
}
// 我们的装饰者类
public abstract class Decorator extends AbstractChickenCutlet {
// 包含了一个抽象构件的引用,可以随时的用某一个具体构件来替换,其次,装饰者类也是继承自抽象构件,不论怎么变化,这个都是一个抽象构件的子类。
private AbstractChickenCutlet abstractChickenCutlet;
public Decorator(AbstractChickenCutlet abstractChickenCutlet) {
this.abstractChickenCutlet = abstractChickenCutlet;
}
@Override
public String desc() {
return this.abstractChickenCutlet.desc();
}
@Override
public double cost() {
return this.abstractChickenCutlet.cost();
}
}
// 装饰者类的具体实现类,可以包装具体构件
public class SaltChicken extends Decorator {
public SaltChicken(AbstractChickenCutlet abstractChickenCutlet) {
super(abstractChickenCutlet);
}
@Override
public String desc() {
return super.desc() + ",撒了一点椒盐";
}
@Override
public double cost() {
return super.cost();
}
}
AbstractChickenCutlet chickenCutlet = new SaltChicken(new CuminChicken(new ChickenCutlet()));
System.out.println(chickenCutlet.desc()); // 原味鸡排,加孜然,撒了一点椒盐
System.out.println(chickenCutlet.cost()); // 价格 10元,加配料不要钱
装饰者模式,基本上就是这样一层嵌套一层,一层包装一层。

例子比较简单,方便记忆,主要还是看装饰者模式,在Java中,典型的应用就是I/O的设计。

在Java的IO设计中,InputStream就是我们的抽象构件,提供了抽象方法,需要子类去实现,我们的具体构件就是ByteArrayInputStream、FileInputStream,可以支持从不同的设备中读取流,以及不同的形式读取流。
FilterInputStream就是我们的抽象装饰类,不过在java的源码中,FiterInputStream并不是一个抽象类,是一个具体类。BufferedInputStream都是FilterInputStream的子类,是对具体构件的一种修饰类,在这个上面实现了带有缓冲区的流。下面列举几个常用的输入流:
| 具体构件类 | 描述 |
|---|---|
| ByteArrayInputStream | 读取数组中的数据当做一个InputStream |
| FileInputStream | 读取文件,将文件当做一个流 |
| PipedInputStream | 管道流,可以应用于多个线程之间,通过管道发送数据,另一个线程通过管道接收数据,实现线程间的通讯 |
| BufferInputStream | 缓冲流,增加缓冲功能,缓冲池大小为8192 |
输出流跟输入流的结构类似,也是一个FilterOutStream作为装饰者,继承OutStream类。
写到最后,装饰者模式,比直接继承一个类,要方便的多,而且比较灵活,用这种嵌套的方式就能解决大部分的问题。其次,装饰者和具体构件,两个是独立的,仅仅是通过中间的抽象构件类进行连接,以组合的方式,形成功能更为复杂的对象。
当然,每一个东西都不是没有缺点的:装饰者模式的缺点,就是每种装饰者都要生成一个装饰具体类,如果装饰很多的话,会产生比较多的对象,在一定程度上会影响性能。其次,层级越多,装饰次数越多,复杂度越高,排查问题时,越难排查。
适用场景:
1、在不影响其他对象的前提下,可以动态的、透明的给单个对象添加职责。 2、如果一个类不能被继承,比如说是final类,要对这个类添加一些行为,可以采用这种方式。