设计模式之装饰者模式
I. 装饰者模式概述
装饰者模式(Decorator Pattern):
- 结构型模式
- 装饰模式就是使用被装饰类的一个子类的实例,在客户端将这个子类的实例交给装饰类
- 是继承的替代方案
- 装饰者模式通过组合的方式扩展对象的特性
- 允许我们在任何时候对对象的功能进行扩展甚至是运行时扩展
- 用继承来完成对类的扩展则只能在编译阶段实现,所以在某些时候装饰者模式比继承要更加灵活
II. 装饰者模式的特征
- 装饰者(decorator)和被装饰(扩展)的对象有着相同的超类(supertype)。
- 我们可以用多个装饰者去装饰一个对象。
- 我们可以用装饰过的对象替换代码中的原对象,而不会出问题(因为他们有相同的超类)。
- 装饰者可以在委托(delegate,即调用被装饰的类的成员完成一些工作)被装饰者的行为完成之前或之后加上他自己的行为。
- 一个对象能在任何时候被装饰,甚至是运行时。
III. 装饰者模式的组成
Component
:- 一般是一个抽象类(也有可能不是),是一组有着某种用途类的基类,包含着这些类最基本的特性。
ConcreteComponent
:- 继承自Component
- 一般是一个有实际用途的类,这个类就是我们以后要装饰的对象。
Decorator
:- 继承自Component
- 装饰者需要共同实现的接口(也可以是抽象类),用来保证装饰者和被装饰者有共同的超类,并保证每一个装饰者都有一些必须具有的性质,如每一个装饰者都有一个实例变量用来保存某个Component类型的类的引用。
ConcreteDecorator
:- 继承自Decorator,用来装饰Component类型的类(不能装饰抽象类),为其添加新的特性
- 可以在委托被装饰者的行为完成之前或之后的任意时候。
IV. 装饰者模式的优缺点
- 优点
- 使用装饰模式,可以提供比继承更灵活的扩展对象的功能,它可以动态的添加对象的功能,并且可以随意的组合这些功能
- 缺点
- 正因为可以随意组合,所以就可能出现一些不合理的逻辑
- 会引入大量的类(比如 Java IO 中的 API)
V. 装饰者模式例子
我们来看这样一个例子,定了一个手机接口 Phone
, 在这个接口里定义了打电话的方法,然后有 IPone 和 Note7 两个实现类分别实现打电话的方法。如果,我要让手机加入打电话听彩铃的功能,或者打电话后听音乐的功能,该怎么实现?
解决方法之一是,修改 IPhone 和 Note7 的实现方法,加入功能,如果这么做,正常的没有彩铃功能的手机怎么办?这就违反了开闭原则。
所以我们可以用装饰者模式,来装饰 Phone
, 给 Phone
添加新的功能,图示如下:
那么首先我们来定义手机接口和两个实现类 IPone 和 Note7
package org.lovian.designpattern.decorator;
public interface Phone {
void call();
String getPhoneName();
}
package org.lovian.designpattern.decorator;
public class IPhone implements Phone {
private String name = "IPhone 6s";
@Override
public void call() {
System.out.println(this.name + " call");
}
@Override
public String getPhoneName(){
return this.name;
}
}
package org.lovian.designpattern.decorator;
public class Note7 implements Phone{
private String name = "Note7";
@Override
public void call() {
System.out.println(this.name + " call");
}
@Override
public String getPhoneName() {
return this.name;
}
}
然后我们定义装饰者抽象类,实现 Phone 接口, 传入一个 Phone
的实现类对象作为被装饰对象
package org.lovian.designpattern.decorator;
public abstract class PhoneDecorator implements Phone{
private Phone phone;
public PhoneDecorator(Phone phone){
this.phone = phone;
}
@Override
public void call() {
this.phone.call();
}
@Override
public String getPhoneName() {
return this.phone.getPhoneName();
}
}
然后我们提供彩铃装饰类,继承抽象装饰者类:
package org.lovian.designpattern.decorator;
public class RingBackMusicPhoneDecorator extends PhoneDecorator{
public RingBackMusicPhoneDecorator(Phone phone) {
super(phone);
}
@Override
public void call() {
System.out.println(super.getPhoneName() + " plays Ringback music");
super.call();
}
}
同样的,提供音乐装饰类
package org.lovian.designpattern.decorator;
public class MusicPhoneDecorator extends PhoneDecorator {
public MusicPhoneDecorator(Phone phone) {
super(phone);
}
@Override
public void call() {
super.call();
System.out.println(super.getPhoneName() + " plays music");
}
}
然后测试类来测试这几个类:
package org.lovian.designpattern.decorator;
public class PhoneTest {
public static void main(String[] args) {
// Create an IPhone
Phone iPhone6s = new IPhone();
iPhone6s.call();
System.out.println("---------------");
// Play RingbackMusic before IPhone's call
PhoneDecorator pd1 = new RingBackMusicPhoneDecorator(iPhone6s);
pd1.call();
System.out.println("---------------");
// Play music after IPhone's call
PhoneDecorator pd2 = new MusicPhoneDecorator(iPhone6s);
pd2.call();
System.out.println("---------------");
// Play RingbackMusic before IPhone's call
// And Play music after IPhone's call
PhoneDecorator pd3 = new RingBackMusicPhoneDecorator(new MusicPhoneDecorator(iPhone6s));
pd3.call();
System.out.println("---------------");
// Create a Note7
Phone note7 = new Note7();
note7.call();
System.out.println("---------------");
// Play RingbackMusic before Note7's call
PhoneDecorator pd4 = new RingBackMusicPhoneDecorator(note7);
pd4.call();
System.out.println("---------------");
// Play Music before Note7's call
PhoneDecorator pd5 = new MusicPhoneDecorator(note7);
pd5.call();
System.out.println("---------------");
// Play RingbackMusic before Note7's call
// And Play music after Note7's call
PhoneDecorator pd6 = new RingBackMusicPhoneDecorator(new MusicPhoneDecorator(note7));
pd6.call();
}
}
result:
IPhone 6s call
---------------
IPhone 6s plays Ringback music
IPhone 6s call
---------------
IPhone 6s call
IPhone 6s plays music
---------------
IPhone 6s plays Ringback music
IPhone 6s call
IPhone 6s plays music
---------------
Note7 call
---------------
Note7 plays Ringback music
Note7 call
---------------
Note7 call
Note7 plays music
---------------
Note7 plays Ringback music
Note7 call
Note7 plays music
我们可以看到,我们不需要改 Phone
的接口,也不需要更改其实现类的方法实现,就可以实现添加功能,这个就是装饰者模式
VI. 装饰者模式在 Java IO 中的应用
我们直到 IO 的 API, 分为字节流和字符流两种,比如 InputStream
就是一个字节流,来读取字节:
InputStream is = System.in;
而如果我们想要其读取字符呢?就要装饰一下这个 is
:
InputStreamReader isr = new InputStreamReader(is);
现在,这个 isr
可以读取字符了,但不能直接读取字符串,那我们再装饰一下这个 isr
:
BufferedReader br = new BufferedReader(isr);
得到的这个 br
就可以直接读取字符串了。再使用的时候,我们如果这样写:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
这个 br 就可以直接获取键盘输入了。由此可知, Java 中 Scanner
类,其实也是 IO 的一个装饰类
Share this on