Java设计模式之装饰器模式
模式定义
装饰器模式允许向一个现有对象添加新的功能,同时又不改变其结构。装饰者可以在所委托被装饰者的行为之前或之后加上自己的行为,以达到自己的目的。
应用场景
需要为一个类动态的添加功能而又不想增加类型体系的时候。
模式角色
Component,抽象构件角色,I/O系统里由InputStream和OutputStream这两个抽象类来担任(注意:Component是用来定义真实对象和装饰对象间相同的接口的)。
ConcreteComponent,具体构件角色(真实对象),I/O系统里由FileInputStream、FileOutputStream等担任(它们要么实现了Component,要么继承了Component)。
Decorator,装饰角色,I/O系统里由FilterInputStream和FilterOutputStream担任,它持有一个Component的引用(具体指的是InputStream和OutputStream)。
ConcreteDecorator,具体装饰角色(Decorator直接子类),I/O系统里由BufferedInputStream或BufferedOutputStream等担任)
举个例子
汉堡女王开了一个汉堡店,主要买以下三种汉堡,这三种汉堡如下:
- WhopperBurger,大汉堡,售价1美元
- SubmarineSandwich,潜艇三明治,售价1.1美元
- Sandwich,三明治,售价0.9美元
同时还提供以下调味品:
- Cheese,奶酪,售价0.9美元
- Beef,牛肉,售价1.6美元
- Bacon,熏猪肉,售价1.5美元
- Chicken,鸡肉,售价1.9美元
- Lettuce,生菜,售价0.7美元
这部开始营业了,客人A点了份潜艇三明治,加牛肉;客人B点了份三明治,加奶酪;客人C点了份大汉堡,加鸡肉和生菜;客人D点了份大汉堡,加熏猪肉和奶酪,要记录点的都是什么,以及每个客人该付多少钱。
代码实现
抽象构件角色
该角色要提供以下功能:
- 记录客人点的是什么;
- 计算客人该付多少钱
抽象去一个接口Burger
package com.rainmonth.pattern.structural.decorator;
public interface Burger {
// 获取食物描述
public String getDescription();
// 获取食物话费
public float cost();
}
具体构件角色
大汉堡
WhopperBurger.java
package com.rainmonth.pattern.structural.decorator;
public class WhopperBurger implements Burger {
public String getDescription() {
return "whopper burger";
}
@Override
public float cost() {
return 1.0f;
}
}
潜艇三明治
SubmarineSandwich.java
package com.rainmonth.pattern.structural.decorator;
public class SubmarineSandwich implements Burger {
@Override
public String getDescription() {
return "submarine sandwich";
}
@Override
public float cost() {
return 1.10f;
}
}
三明治
Sandwich.java
package com.rainmonth.pattern.structural.decorator;
public class Sandwich implements Burger {
public String getDescription() {
return "sandwich";
}
@Override
public float cost() {
return 0.9f;
}
}
抽象装饰器角色
调味品类(装饰器基类)
CondimentDecorator.java
package com.rainmonth.pattern.structural.decorator;
public abstract class CondimentDecorator implements Burger {
protected Burger burger;
public CondimentDecorator(Burger burger) {
this.burger = burger;
}
}
具体装饰器角色
熏猪肉,Bacon.java
package com.rainmonth.pattern.structural.decorator;
public class Bacon extends CondimentDecorator {
public Bacon(Burger burger) {
super(burger);
}
@Override
public String getDescription() {
return burger.getDescription() + ", Bacon";
}
@Override
public float cost() {
return burger.cost() + 1.5f;
}
}
牛肉,Beef.java
package com.rainmonth.pattern.structural.decorator;
public class Beef extends CondimentDecorator {
public Beef(Burger burger) {
super(burger);
}
@Override
public String getDescription() {
return burger.getDescription() + ", Beef";
}
@Override
public float cost() {
return burger.cost() + 1.6f;
}
}
奶酪,Cheese.java
package com.rainmonth.pattern.structural.decorator;
public class Cheese extends CondimentDecorator {
public Cheese(Burger burger) {
super(burger);
}
@Override
public String getDescription() {
return burger.getDescription() + ", Cheese";
}
@Override
public float cost() {
return burger.cost() + 0.9f;
}
}
鸡肉,Chicken.java
package com.rainmonth.pattern.structural.decorator;
public class Chicken extends CondimentDecorator {
public Chicken(Burger burger) {
super(burger);
}
@Override
public String getDescription() {
return burger.getDescription() + ", Chicken";
}
@Override
public float cost() {
return burger.cost() + 1.9f;
}
}
生菜,Lettuce.java
package com.rainmonth.pattern.structural.decorator;
public class Lettuce extends CondimentDecorator {
public Lettuce(Burger burger) {
super(burger);
}
@Override
public String getDescription() {
return burger.getDescription() + ", Lettuce";
}
@Override
public float cost() {
return burger.cost() + 0.7f;
}
}
模拟客户点餐
package com.rainmonth.pattern.structural.decorator;
public class BurgerQueen {
public static void main(String[] args) {
// 潜艇三明治,加牛肉
Burger submarineSandwichBeef = new Beef(new SubmarineSandwich());
System.out.println(submarineSandwichBeef.getDescription());
System.out.println(submarineSandwichBeef.cost() + "\n");
// 三明治,加奶酪
Burger cheeseSandwich = new Cheese(new Sandwich());
System.out.println(cheeseSandwich.getDescription());
System.out.println(cheeseSandwich.cost() + "\n");
// 大汉堡,加生菜和鸡肉
Burger lettuceChickenWhopper = new Lettuce(new Chicken(new WhopperBurger()));
System.out.println(lettuceChickenWhopper.getDescription());
System.out.println(lettuceChickenWhopper.cost() + "\n");
// 大汉堡,加熏猪肉和奶酪
Burger baconCheeseWhopper = new Bacon(new Cheese(new WhopperBurger()));
System.out.println(baconCheeseWhopper.getDescription());
System.out.println(baconCheeseWhopper.cost() + "\n");
}
}
输出如下
submarine sandwich, Beef
2.7
sandwich, Cheese
1.8
whopper, Chicken, Lettuce
3.6000001
whopper, Cheese, Bacon
3.4
Process finished with exit code 0
UML图
上面例子的简单版UML图如下:
带方法的UML图如下:
总结
从上面的例子及UML图,可以看出装饰器模式有如下优缺点:
优点
- 可以很好的扩展功能,具体表现就是可以随意的为一款主食(大汉堡、潜艇三明治、三明治)添加调味品(Bacon、Beef等)
- ConfidentDecorator和具体的Bacon、Beef等没有发生耦合
缺点
- 很明显,我要加多个调味品的话,就要新建多个对象(多个new),在一定程度上增加了对象使用的复杂行