所谓的装饰者模式是指:在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
在装饰模式中的各个角色有:
(1)抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
(2)具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。
(3)装饰(Decorator)角色:持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
(4)具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
最常见的例子就是Java中的IO流:
FileInputStream in=new FileInputStream("name.text");
BufferedInputStream bufferedIn=new BufferedInputStream(in);
这里的FileInputStream就是具体构建,BufferedInputStream就是具体装饰。而InputStream 则是抽象构建。
下面我们拿咖啡馆售卖咖啡来举个例子:
咖啡是个抽象构建,不同种类的咖啡是具体构建。调料是抽象装饰,不同种类的调料(牛奶,糖,巧克力,拉花等等)是具体抽象装饰;
首先创建抽象Coffee类:
abstract class Coffee {
private String name;
public Coffee(String name){
this.name=name;
}
public String getName(){
return name;
}
public void getInfo(){
System.out.println(getName()+cost());
}
abstract double cost();
}
咖啡类具有一个抽象的价格方法,每个种类的价钱不同。并且有一个返回自己名称的方法,以及输出该种咖啡名称和价格的getInfo()方法;
接下来我们实现具体的几种咖啡类;
第一种蓝山咖啡:
class BlueMountainCoffee extends Coffee{
public BlueMountainCoffee(String name) {
super(name);
// TODO Auto-generated constructor stub
}
@Override
double cost() {
return 2.5;
}
}
第二种浓缩咖啡:
class Espresso extends Coffee{
public Espresso(String name) {
super(name);
// TODO Auto-generated constructor stub
}
@Override
double cost() {
return 1;
}
}
接着到了最重要的一步,创建抽象装饰:
abstract class Seasoning extends Coffee{
private Coffee coffee;
public Seasoning(String name,Coffee coffee){
super(name+coffee.getName());
this.coffee=coffee;
}
@Override
abstract double cost();
}
为了让装饰类能够装饰具体构建内,所以在上面代码里,我们让抽象装饰持有了一个抽象构建。
最后实现具体装饰,例如加糖,牛奶,拉花等等:
class AddSugar extends Seasoning{
public AddSugar(String name, Coffee coffee) {
super(name, coffee);
// TODO Auto-generated constructor stub
}
//加糖多收一块钱
@Override
double cost() {
return 1+coffee.cost();
}
}
class AddMilk extends Seasoning{
public AddMilk(String name, Coffee coffee) {
super(name, coffee);
// TODO Auto-generated constructor stub
}
//加牛奶要多收2元
@Override
double cost() {
// TODO Auto-generated method stub
return 2+coffee.cost();
}
}
class AddLahua extends Seasoning{
public AddLahua(String name, Coffee coffee) {
super(name, coffee);
// TODO Auto-generated constructor stub
}
//加拉花要1.5
@Override
double cost() {
// TODO Auto-generated method stub
return 1.5+coffee.cost();
}
}
最后我们实际测试一下:
public static void main(String[] args) {
BlueMountainCoffee bmCoffee=new BlueMountainCoffee("蓝山咖啡");
//顾客需要一杯加糖加拉花的蓝山咖啡
AddSugar addSugar=new AddSugar("加糖",bmCoffee);
AddLahua addLahua=new AddLahua("加拉花",addSugar);
addLahua.getInfo();
//顾客需要一杯加糖加牛奶的浓缩咖啡
Espresso espresso=new Espresso("浓缩咖啡");
AddSugar addSugar2=new AddSugar("加糖", espresso);
AddMilk addMilk=new AddMilk("加牛奶", addSugar2);
addMilk.getInfo();
}
可以看到我们不管是怎么随意对咖啡对象装饰调料都能得出正确的结果。
使用装饰者模式和继承进行对比我们就能发现其优点:
同样还是咖啡,现在不仅有三种调味了(调味装饰接口),还有大中小三种规格(大小装饰接口)。
如果使用继承实现,我们为了满足顾客能点到所有种类的咖啡,则必须实现例如加糖小,加糖加牛奶小,加糖加牛奶加拉花小…总共九种子类。如果有更多选项,那么通过继承实现的工作量将会变得更大。
而使用装饰者模式,我们只需实现需要的装饰类。然后客户想怎么组合,自行随意封装即可。
完整代码:
public class Begin {
public static void main(String[] args) {
BlueMountainCoffee bmCoffee=new BlueMountainCoffee("蓝山咖啡");
//顾客需要一杯加糖加拉花的蓝山咖啡
AddSugar addSugar=new AddSugar("加糖",bmCoffee);
AddLahua addLahua=new AddLahua("加拉花",addSugar);
addLahua.getInfo();
//顾客需要一杯加糖加牛奶的浓缩咖啡
Espresso espresso=new Espresso("浓缩咖啡");
AddSugar addSugar2=new AddSugar("加糖", espresso);
AddMilk addMilk=new AddMilk("加牛奶", addSugar2);
addMilk.getInfo();
}
}
abstract class Coffee {
private String name;
public Coffee(String name){
this.name=name;
}
public String getName(){
return name;
}
public void getInfo(){
System.out.println(getName()+cost());
}
abstract double cost();
}
class BlueMountainCoffee extends Coffee{
public BlueMountainCoffee(String name) {
super(name);
// TODO Auto-generated constructor stub
}
@Override
double cost() {
return 2.5;
}
}
class Espresso extends Coffee{
public Espresso(String name) {
super(name);
// TODO Auto-generated constructor stub
}
@Override
double cost() {
return 1;
}
}
abstract class Seasoning extends Coffee{
public Coffee coffee;
public Seasoning(String name,Coffee coffee){
super(name+coffee.getName());
this.coffee=coffee;
}
@Override
abstract double cost();
}
class AddSugar extends Seasoning{
public AddSugar(String name, Coffee coffee) {
super(name, coffee);
// TODO Auto-generated constructor stub
}
//加糖多收一块钱
@Override
double cost() {
return 1+coffee.cost();
}
}
class AddMilk extends Seasoning{
public AddMilk(String name, Coffee coffee) {
super(name, coffee);
// TODO Auto-generated constructor stub
}
//加牛奶要多收2元
@Override
double cost() {
// TODO Auto-generated method stub
return 2+coffee.cost();
}
}
class AddLahua extends Seasoning{
public AddLahua(String name, Coffee coffee) {
super(name, coffee);
// TODO Auto-generated constructor stub
}
//加拉花要1.5
@Override
double cost() {
// TODO Auto-generated method stub
return 1.5+coffee.cost();
}
}