装饰者模式,可以动态地把职责附加到已有的对象上面去。又称 Wrapper Pattern,在代码实现的时候,确实有“包装”的意思。
# 类图
![装饰者](https://box.kancloud.cn/2016-02-22_56cab10d6335a.jpg "")
图:来自《Head First Design Patterns》
可以看出,装饰者模式里面,有4个角色:Component抽象类,ConcreteComponent具体类,Decorator抽象类,ConcreteDecorator具体类
![装饰者模式](https://box.kancloud.cn/2016-02-22_56cab10d8b732.jpg "")
图:我的Java源代码的类图
总共10个类
一个Component抽象类
两个ConcreteComponent具体类
一个Decorator抽象类
五个ConcreteDecorator具体类
一个Main测试类
ConcreteDecorator **继承**了Decorator抽象类, **组合**了Component抽象类。Decorator抽象类继承了Component抽象类,这个继承的目的是:it’s vital that the decorators have the same type as the object they are going to decorate。简而言之,就是为了获得相同的类型,并不是为了获得行为。组合是为了让所有的concreteDecorator可以“互相包装、装饰”
# talk is cheap, show me the code
去食堂打菜,经济套餐,假设总共只有5种食物可供选择分别是:手撕包菜,茄子豆角,麻婆豆腐,青椒肉丝,红烧里脊。
### 一个Component抽象类
~~~
package decorator;
public abstract class Meal
{
String description = "Unknown Meal";
public String getDescription()
{
return description;
}
public abstract double cost();
}
~~~
### 两个ConcreteComponent具体类
~~~
package decorator;
public class ChineseMeal extends Meal
{
public ChineseMeal()
{
description = "Chinese Meal";
}
@Override
public double cost()
{
return 1.0; // 一次性餐具费用
}
}
~~~
~~~
package decorator;
public class WesternMeal extends Meal
{
public WesternMeal()
{
description = "Western meal";
}
@Override
public double cost()
{
return 5.0; // 不锈钢刀、叉费用
}
}
~~~
### 一个Decorator抽象类
~~~
package decorator;
public abstract class Decorator extends Meal
{
public abstract String getDescription();
}
~~~
### 五个ConcreteDecorator具体类
~~~
package decorator;
// 茄子豆角
public class EggplantBean extends Decorator
{
Meal meal;
public EggplantBean(Meal meal)
{
this.meal = meal;
}
@Override
public String getDescription()
{
return meal.getDescription() + ", 茄子豆角";
}
@Override
public double cost()
{
return 2 + meal.cost();
}
}
~~~
~~~
package decorator;
// 青椒炒肉
public class GreenPepperPork extends Decorator
{
Meal meal;
public GreenPepperPork(Meal meal)
{
this.meal = meal;
}
@Override
public String getDescription()
{
return meal.getDescription() + ", 青椒炒肉";
}
@Override
public double cost()
{
return 3 + meal.cost();
}
}
~~~
~~~
package decorator;
// 手撕包菜
public class HandCabbage extends Decorator
{
Meal meal;
public HandCabbage(Meal meal)
{
this.meal = meal;
}
@Override
public String getDescription()
{
return meal.getDescription() + ", 手撕包菜";
}
@Override
public double cost()
{
return 2 + meal.cost();
}
}
~~~
~~~
package decorator;
// 麻婆豆腐
public class MapoTofu extends Decorator
{
Meal meal;
public MapoTofu(Meal meal)
{
this.meal = meal;
}
@Override
public String getDescription()
{
return meal.getDescription() + ", 麻婆豆腐";
}
@Override
public double cost()
{
return 4 + meal.cost();
}
}
~~~
~~~
package decorator;
// 红烧里脊
public class BraiseTenderloin extends Decorator
{
Meal meal;
public BraiseTenderloin(Meal meal)
{
this.meal = meal;
}
@Override
public String getDescription()
{
return meal.getDescription() + ", 红烧里脊";
}
@Override
public double cost()
{
return 5 + meal.cost();
}
}
~~~
### 一个Main测试类
~~~
package decorator;
public class Main
{
public static void main(String[] args)
{
Meal meal = new ChineseMeal();
meal = new GreenPepperPork(meal);
meal = new HandCabbage(meal);
meal = new BraiseTenderloin(meal);
System.out.println(meal.getDescription() + " ¥" + meal.cost());
Meal meal2 = new ChineseMeal();
meal2 = new EggplantBean(new MapoTofu(new BraiseTenderloin(meal2)));
System.out.println(meal2.getDescription() + " ¥" + meal2.cost());
}
}
~~~
# 运行结果
直接从eclipse复制过来
~~~
Chinese Meal, 青椒炒肉, 手撕包菜, 红烧里脊 ¥11.0
Chinese Meal, 红烧里脊, 麻婆豆腐, 茄子豆角 ¥12.0
~~~
# 分析讨论
去食堂打菜,经济套餐,假设总共只有5种食物可供选择分别是:手撕包菜,茄子豆角,麻婆豆腐,青椒肉丝,红烧里脊。如果可以随意打菜,就可以有C15+C25+C35+C45+C55=25−1=31 种选择。如果写代码把这120种选择都分别弄成一个类,就有31个类。这样不仅代码重复率太高,而且如果食堂**更改**了上述五样菜中一样菜的**价格**,那么**31个类的代码需要全部修改**。这不符合开闭原则,不利于代码的维护和扩展。
装饰者模式就可以解决上述这种“类的爆炸式增长”问题,各种各样的排列组合太多,不能在代码中一个类一个。
装饰者模式的特点是“继承(inheritance) + 组合(Composition)”:继承是为了让component和decorator拥有相同的类型,组合是为了让所有的concreteDecorator可以“互相包装、装饰”。装饰者又名Wrapper大概就是这样来的。
# Java. IO
Java.IO 中应用了装饰者模式
~~~
FileInputStream fiStream = null;
InputStreamReader iStreamReader = null;
BufferedReader bReader = null;
fiStream = new FileInputStream("C:\\xxxx");
// InputStreamReader 是字节流通向字符流的桥梁、
iStreamReader = new InputStreamReader(fiStream);
// 从字符输入流中读取文件中的内容,装饰了一个InputStreamReader的对象
bReader = new BufferedReader(iStreamReader("C:\\xxxx"));
~~~
~~~
一句代码版本,把3个new放到一起
BufferedReader bReader = new BufferedReader(new InputStreamReader(new FileInputStream("C:\\xxxx"));
~~~