装饰模式
目的
动态地给一个对象增加一些额外的职责。
类图
角色
- Component: 抽象构件
- ConcreteComponent: 具体构件 :用于定义具体的构件对象,实现了在抽象构件中声明的方法。
- Decorator: 抽象装饰类:用于给具体构件增加职责,但是具体职责在其子类中实现。
- ConcreteDecorator: 具体装饰类:抽象装饰类的子类,负责向构件添加新的职责。
实现
抽象构件
使用奶茶的例子来说, 汉堡
是一个 Component 抽象构件,有一个方法 cost
负责计算价格。
public interface Hamburger {
double cost();
}
具体构件
汉堡分很多种,鸡肉的牛肉的,这些都可以当做 ConcreteComponent
。
public class BeefHamburger implements Hamburger {
@Override
public double cost() {
return 1;
}
}
public class ChickenHamburger implements Hamburger {
@Override
public double cost() {
return 1;
}
}
抽象装饰类
汉堡光有肉还不行,可能顾客还要加点别的,比如芝士、火腿。
public abstract class HamburgerDecorator implements Hamburger {
protected Hamburger hamburger;
}
具体装饰类
接着就是加料了
public class Cheese extends HamburgerDecorator {
public Cheese(Hamburger hamburger) {
this.hamburger = hamburger;
}
@Override
public double cost() {
return 1 + hamburger.cost();
}
}
public class Ham extends HamburgerDecorator {
public Ham(Hamburger hamburger) {
this.hamburger = hamburger;
}
@Override
public double cost() {
return 1 + hamburger.cost();
}
}
调用
最后,让我们来做一个牛肉+芝士的汉堡吧。
public class Client {
public static void main(String[] args) {
Hamburger hamburger = new BeefHamburger();
hamburger = new Cheese(hamburger);
System.out.println(hamburger.cost());
}
}
优缺点
优点
- 可以提供比继承更多的灵活性。
- 可以动态扩展一个对象的功能,比如通过配置文件在运行时选择不同的装饰器。
- 可以创造出很多不同行为的组合。使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
- 根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。
缺点
- 产生很多小对象,区别在于之间相互连接方式不同,而不是类或属所不,还将产生很多具体装饰类。增加系统的复杂度与理解的难度。
- 比继承更加易于出错,对于多次装饰的对象,寻找错误需要逐级排查。
使用场景
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。
- 当不能采用继承的方式对系统进行扩充或采用继承不利于系统扩展和维护时。
- 系统中存在大量独立扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;
- 第二类是因为类定义不能继承(如final类).
和桥接模式的区别
角度不同
- 装饰模式:动态地添加一些额外功能模式,是适应新需求而添加新功能,并且不影响其他对象的一种模式;
- 桥接模式:是适应变化维度的模式,在于将对象的各个维度的变化都独立开,使一些变化不受其他因素变化的影响。
独立不同
- 装饰模式:实现的是不同功能的子类进行封闭后独立的子类,但仍旧是紧耦合(因为是继承方式),
- 桥接模式:将变化独立开,降低类之间的耦合度,尽最大可能实现松耦合(组合方式)。
使用
- java.io.BufferedInputStream(InputStream)
- java.io.DataInputStream(InputStream)
- java.io.BufferedOutputStream(OutputStream)
- java.util.zip.ZipOutputStream(OutputStream)
- java.util.Collections