一、概述
装饰器模式是一种结构型设计模式,可以做到在不改变原来对象功能的情况下,向原有的对象添加新的功能,起到一个装饰的作用。具体的做法是创建一个装饰器类,用来包装原有的类,在不改变原有类方法的情况下,为原有类添加新的功能。
- 1、什么时候使用
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责;
- 处理那些可以撤销的职责;
- 当不能采用生成子类的方式进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
- 2、四大角色
1)抽象装饰对象角色(Component):接口,被装饰对象的基类,提供被装饰得方法,被装饰对象和装饰器都实现该接口;
2)具体装饰对象角色(ConcreteComponent):具体被装饰对象,它继承自Component,并实现需要被装饰的方法基本内容;
3)抽象装饰器角色(Decorator):抽象类,维持一个装饰对象实例的引用,所有具体装饰器对象的父类,完成装饰器的部分职能。可以只对被装饰的对象进行一些简单的包裹,也可包含对被装饰的对象中方法的实现;
4)具体装饰器角色(ConcreteDecorator):完成具体的装饰功能。装饰功能的实现是通过调用被装饰对象对应的方法,加上装饰对象自身的方法,这是装饰器模式动机中的添加额外功能的关键。可以有多个具体装饰角色,但是要注意各装饰之间的调用顺序。
二、代码示例
来看一个例子,我们在外面吃饭,有很多食物,其中有烧烤和火锅。比如我们点了烧烤,但是觉得味道不够爽,所以我们选择让老板加盐,或者加辣椒,这里的加盐和加辣椒其实就是对事物起装饰作用。用代码实现如下:
1、抽象装饰对象角色(Component):创建Food接口
/**
* Created by yd on 2019/4/2.
* 抽象装饰对象角色(Component):接口,被装饰对象的基类,提供被装饰得方法,被装饰对象和装饰器都实现该接口
*/
public interface Food {
String getDesc();
String getCost();
}
2、具体装饰对象角色(ConcreteComponent):创建实现接口的实现类
Barbecue实现类
/**
* Created by yd on 2019/4/2.
* 具体装饰对象角色(ConcreteComponent):具体被装饰对象,它继承自Component,并实现需要被装饰的方法基本内容;
*/
public class BarbecueFood implements Food {
@Override
public String getDesc() {
return "烧烤";
}
@Override
public String getCost() {
Double cost = 3.0;
return String.format("最后价格:%s块", cost);
}
}
Hotpot实现类
/**
* Created by qxr4383 on 2019/4/2.
* 抽象装饰对象角色(Component):接口,被装饰对象的基类,提供被装饰得方法,被装饰对象和装饰器都实现该接口
*/
public class HotpotFood implements Food {
@Override
public String getDesc() {
return "火锅";
}
@Override
public String getCost() {
Double cost = 100.0;
return String.format("最后价格:%s块", cost);
}
}
3、抽象装饰器角色(Decorator):创建装饰器基类
FoodDecorator是一个抽象类,其中组合Food类
/**
* Created by qxr4383 on 2019/4/2.
* FoodDecorator是一个抽象类,其中组合Food类
*/
public abstract class FoodDecorator implements Food{
protected Food food;
public FoodDecorator(Food food) {
this.food = food;
}
public String getDesc(){
return food.getDesc();
}
public String getCost(){
return food.getCost();
}
}
4、具体装饰器角色(ConcreteDecorator)
创建SaltFoodDecorator装饰器类
/**
* Created by yd on 2019/4/2.
*创建SaltFoodDecorator装饰器类
*/
public class SaltFoodDecorator extends FoodDecorator {
public SaltFoodDecorator(Food food){
super(food);
}
@Override
public String getDesc() {
String result = "加盐的" + food.getDesc();
return result;
}
public String getCost() {
Double salt = 2.0;
System.out.println(String.format("加盐多收%s块", salt));
String result = food.getCost() + " + " + salt + "块";
return result;
}
}
创建PepperFoodDecorator装饰器类
/**
* Created by yd on 2019/4/2.
* 创建PepperFoodDecorator装饰器类
*/
public class PepperFoodDecorator extends FoodDecorator {
public PepperFoodDecorator(Food food){
super(food);
}
@Override
public String getDesc() {
String result = "加盐的" + food.getDesc();
return result;
}
@Override
public String getCost() {
Double pepper = 10.0;
System.out.println(String.format("加辣椒多收%s块", pepper));
String result = food.getCost() + " + " + pepper + "块";
return result;
}
}
最后来测试一下
/**
* Created by qxr4383 on 2019/4/2.
*/
public class Test {
public static void main(String[] args) {
//创建不用装饰器修饰的Food
Food food = new BarbecueFood();
display(food.getDesc());
display(food.getCost());
System.out.println("-------------分割线---------------");
//创建用SaltFoodDecorator装饰的Food
Food barbecue = new SaltFoodDecorator(new BarbecueFood());
display(barbecue.getDesc());
display(barbecue.getCost());
System.out.println("-------------分割线---------------");
//创建用PepperFoodDecorator装饰的Food
Food hotSpot = new PepperFoodDecorator(new HotpotFood());
display(hotSpot.getDesc());
display(hotSpot.getCost());
}
private static void display(Object obj) {
System.out.println(obj);
}
}
执行结果:
烧烤
最后价格:3.0块
-------------分割线---------------
加盐的烧烤
加盐多收2.0块
最后价格:3.0块 + 2.0块
-------------分割线---------------
加盐的火锅
加辣椒多收10.0块
最后价格:100.0块 + 10.0块
来看一下类图会更加地清晰
END
装饰器模式在日常开发中也有很多应用,典型的就是JDK里面的IO。
InputStream代表输入流,输入来源可以是文件(FileInputStream)、管道(PipedInputStream)、数组(ByteArrayInputStream)等。就像上面的烧烤,火锅。FilterInputStream就是装饰器的基类,他的实现类是一系列的装饰器,比如BufferedInputStream可以用缓冲区来修饰InputStream,把InputSteam包装成有缓冲区的输入流。