装饰模式是在不必改变原类文件和使用继承的情况下, 动态地扩展一个对象的功能, 它是通过创建一个包装对象, 也就是装饰来包裹真实的对象.
装饰者模式在JAVA的IO中应用的很广泛:FilterInputStream 和 FilterOutputStream就是典型的应用
一个生活中常见的例子:盖浇饭
我们中午出去吃午饭,可能有人想吃米饭,有人想吃面条,还有人想喝粥等等,这个算是主食
此外还会点一些浇头,比如荷包蛋,青菜,大排等, 这些浇头就是装饰者了
先定义一个食物的接口Food:
public interface Food {
public String getDesc();
public double getPrice();
}
public class Noodle implements Food{
@Override
public String getDesc() {
return " 面条 ";
}
@Override
public double getPrice() {
return 5;
}
}
public class Rice implements Food{
@Override
public String getDesc() {
return "米饭";
}
@Override
public double getPrice() {
return 2;
}
}
public class Porridge implements Food{
@Override
public String getDesc() {
return " 粥 ";
}
@Override
public double getPrice() {
return 3;
}
}
接下来定义一个浇头的抽象类:
public abstract class Garnish implements Food{
public Food food;
public Garnish(Food food) {
this.food = food;
}
@Override
public String getDesc() {
return food.getDesc();
}
@Override
public double getPrice() {
return food.getPrice();
}
}
接着就是浇头的具体实现类:
public class Vegetable extends Garnish{
public Vegetable(Food food) {
super(food);
}
@Override
public String getDesc() {
return "青菜+" + food.getDesc();
}
@Override
public double getPrice() {
return 4 + super.getPrice();
}
}
public class Ribs extends Garnish{
public Ribs(Food food) {
super(food);
}
@Override
public String getDesc() {
return "大排+" + food.getDesc();
}
@Override
public double getPrice() {
return 8 + super.getPrice();
}
}
public class Agg extends Garnish{
public Agg(Food food) {
super(food);
}
@Override
public String getDesc() {
return "荷包蛋+" + food.getDesc();
}
@Override
public double getPrice() {
return 2 + super.getPrice();
}
}
浇头只负责装饰Food对象, 而不管这个Food是怎么来的, 可以对Food进过多次装饰, 每次装饰, 只加上自身属性, 和被装饰的属性
被装饰的对象,可以是已经被装饰过的对象
客户端实现:
public class FoodTest {
public static void main(String[] args) {
Food food = new Rice();
Food agg = new Agg(food);
Food vegetables = new Vegetable(agg);
System.out.println("名称:" + vegetables.getDesc() + " 价格:" + vegetables.getPrice());
Ribs ribs = new Ribs(agg);
System.out.println("名称:" + ribs.getDesc() + " 价格:" + ribs.getPrice());
}
}
输出结果:
名称:青菜+荷包蛋+米饭 价格:8.0
名称:大排+荷包蛋+米饭 价格:12.0
只需要先点一份主食,然后依次用想要的浇头装饰主食, 每次使用浇头进行装饰, 浇头只知道传进来的是一份主食,而不管是否已经被装饰过
同一份浇头也可以装饰多次
装饰者模式和代理模式的实现有点类似:都是实现同一个接口, 然后利用组合, 在原来对象的方法上添加额外的动作
然而,实际上,在装饰器模式和代理模式之间还是有很多差别的。装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。