概述
定义
指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。
看了网上的很多关于装饰者模式的讲解,都是一些什么关于咖啡算总价的例子,起码看的时候总是会把重心放到它的递归求总价上面,而忘记了装饰者的本质是添加额外的功能。
刚好最近迷上了一款永劫无间的游戏,个人觉得装饰者模式似乎就像武器和魂玉之间的关系。有了魂玉,武器就可以有一些特定的技能。我们以此为基础来理解装饰者模式。
结构
装饰者(Decorator)模式中的角色:
- 抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。(各种武器的父类——Weapon)
- 具体构件(Concrete Component)角色 :实现抽象构件,通过装饰角色为其添加一些职责。(具体武器——长枪)
- 抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。(装饰者)
- 具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。(特定技能——魂玉)
举例
1、定义抽象构件
这里使用抽象类或者接口都是可以的
public abstract class Weapon {
private String name;
public Weapon(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract String display();
}
2、定义具体构件
这里可以定义多个具体武器,由于个人比较喜欢长枪,所以只定义了一个长枪,继承武器类,并实现抽象方法
public class Pike extends Weapon{
public Pike() {
super("长枪");
}
@Override
public String display() {
return "长枪展示:";
}
}
3、定义装饰者
装饰者类为装饰者模式的核心,它继承武器类,并拥有一个构件对象,可以认为这个构件对象才是武器本体。在我看来,装饰者是为了把魂玉‘’装到“武器上,所以必须有一个武器本体。
public abstract class Decorator extends Weapon{
// 持有一个构件对象
protected Weapon weapon;
public Decorator(String name,Weapon weapon) {
super(name);
this.weapon = weapon;
}
@Override
public String display() {
return weapon.display() + " 装配: " + getName() + " 获得能力: " + bisha();
}
// 对组件进行装饰的抽象方法
public abstract String bisha();
}
4、具体装饰角色
定义了具体的功能,比如这个魂玉,使武器拥有了bisha。
public class Jade1 extends Decorator{
public Jade1(Weapon weapon) {
super("魂玉1",weapon);
}
@Override
public String bisha() {
return "大圣游";
}
}
public class Jade2 extends Decorator{
public Jade2(Weapon weapon) {
super("魂玉2",weapon);
}
@Override
public String bisha() {
return "狂狼怒涛";
}
}
5、客户端
public class AppTest {
public static void main(String[] args) {
Weapon pike = new Pike();
pike= new Jade1(pike);
pike= new Jade2(pike);
System.out.println(pike.display());
}
}
这里使用的时候有点类似javaIO流的使用,因为java的IO流的设计就是使用了装饰者模式。
长枪展示: 装配: 魂玉1 获得能力: 大圣游 装配: 魂玉2 获得能力: 狂狼怒涛
类图
优点
- 饰者模式可以带来比继承更加灵活性的扩展功能,使用更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化的结果。装饰者模式比继承更具良好的扩展性,完美的遵循开闭原则,继承是静态的附加责任,装饰者则是动态的附加责任。
- 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
源码分析
如上面所说,装饰者模式在javaIO流中应用比较广泛,像我们平常常见的BufferedInputStream就是一个具体装饰者,它提供的功能就是添加缓冲区,更好的读取流的内容。先来看一下BufferedInputStream的继承关系
对比上面的类图看这个关系是不是更加明显呢?
Decorator就对应着FilterInputStream
在FilterInputStream里也聚合了一个InputStream
再看看BufferedInputStream是如何使用的
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\bis.txt"));
byte[] bytes = new byte[1024];
int read = bis.read(bytes);
System.out.println(bytes+":"+read);
这里没有直接new一个InputStream,而是new一个FileInpuStream,毕竟FileInputStream也是继承InputStream。FileInputStream读取在某些情况下是阻塞的,这里使用缓冲区来读取既能提高读取效率(因为缓冲区在内存里),又能避免阻塞(BufferInputStream的read是非阻塞的)