装饰者模式:给一个对象增加一些新的功能,而且要求是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
假如我们想对房子进行装饰,首先得有一个House接口,里面有装饰的方法,代表是装饰房子这样的一个通用的特性。
House接口:
public interface House {
//刷墙漆
public void paint();
//贴墙纸
public void wallpaper();
}
然后定义一个抽象的装饰类Decorator实现House接口,Decorator类:
public abstract class Decorator implements House {
//持有被装饰对象引用
private House house;
public Decorator(House house){
this.house = house;
}
@Override
public void paint() {
house.paint();
}
@Override
public void wallpaper() {
house.wallpaper();
}
}
接下来就会有三个人分别来装饰房子,他们都要继承抽象的装饰类Decorator。
PersonOne类:
public class PersonOne extends Decorator {
public PersonOne(House house) {
super(house);
}
@Override
public void paint() {
super.paint();
System.out.println("选购墙漆");
}
@Override
public void wallpaper() {
super.wallpaper();
System.out.println("选购墙纸");
}
}
PersonTwo类:
public class PersonTwo extends Decorator {
public PersonTwo(House house) {
super(house);
}
@Override
public void paint() {
super.paint();
System.out.println("墙漆的用量勾兑,搅匀");
}
@Override
public void wallpaper() {
super.wallpaper();
System.out.println("剪裁墙纸");
}
}
PersonThree类:
public class PersonThree extends Decorator{
public PersonThree(House house) {
super(house);
}
@Override
public void paint() {
super.paint();
System.out.println("开始刷墙漆");
}
@Override
public void wallpaper() {
super.wallpaper();
System.out.println("开始贴墙纸");
}
}
三个人要做的工作都已经安排好了,现在我们要选择一个房间来装饰,就选卧室吧
BedRoom类:
public class BedRoom implements House {
@Override
public void paint() {
System.out.println("我要装饰卧室,打算刷点墙漆");
}
@Override
public void wallpaper() {
System.out.println("---------感觉还不太满意");
System.out.println("我还打算贴点墙纸");
}
}
房间也选好了,开始动工吧。
DecoratorTest类:
public class DecoratorTest {
public static void main(String[] args) {
House house = new BedRoom();
Decorator decorator = new PersonThree(new PersonTwo(new PersonOne(house)));
decorator.paint();
decorator.wallpaper();
}
}
输出结果:我要装饰卧室,打算刷点墙漆
选购墙漆
墙漆的用量勾兑,搅匀
开始刷墙漆
---------感觉还不太满意
我还打算贴点墙纸
选购墙纸
剪裁墙纸
开始贴墙纸
这样房间就算装饰完了,大功告成。
其实装饰者模式在java中运用很广泛的,比如IO流中,运用了大量的装饰者模式。以InputStream为例,从装饰者模式来看下InputStream的结构。
InputStream的类图关系
class java.lang.Object
|
|—class java.io.InputStream //输入流,字节形式,为以下的基类
| |
| |——java.io.ByteArrayInputStream //从字节数组中读取
| |
| |——java.io.FileInputStream //从文件中读取数据
| |
| |—— java.io.FileInputStream //过滤流的基类,
| | | // 过滤可以了解为各种处理技术的形象称呼
| | |
| | |——java.io.BufferedInputStream //缓冲技术,数据来自底层输入流
| | |
| | |——java.util.zip.CheckedInputStream //用于维护数据校验和
| | |
| | |——javax.crypto.CipherInputStream //属于Cipher类的扩展,称为密钥流
| | |
| | |——java.io.DataInputStream //可读java数据类型
| | |
| | |——java.util.zip.DeflaterInputStream //压缩数据的输入流
| | |
| | |——java.security.DigestInputStream //消息摘要输入流,可以通过读取输入流的方式完成摘要更新
| | |
| | |——java.util.zip.InflaterInputStream //解压缩 "deflate" 压缩格式的数据实现流过滤器。它还用作其他解压缩过滤器(如 GZIPInputStream)的基础。
| | |
| | |——java.io.LineNumberInputStream //输入流过滤器,提供当前行号的附加功能(已过时)
| | || | |——javax.swing.ProgressMonitorInputStream //监视读取某些 InputStream 的进度
| | |
| | |——java.io.PushbackInputStream //允许我们试探性的读取数据流,如果不是我们想要的则返还回去,之所以能够这样,因为其内部维护了一个pushback buffer缓冲区。
| | |
| | |——java.util.zip.GZIPInputStream //继承InflaterInputStream,读取 GZIP 文件格式的压缩数据实现流过滤器
| | |
| |—— .......
从以上可以看出,InputStream就是装饰者模式中的超类(Component),ByteArrayInputStream、FileInputStream相当于被装饰者(ConcreteComponent),这些类都提供了最基本的字节读取功能。而另外一个和这两个类是同一级的FilterInputStream即是装饰者(Decorator),BufferedInputStream、DataInputStream、PushbackInputStream......这些都是被装饰者装饰后形成的成品。
根据装饰者模式的特点,我们可以总结出这些IO流的使用方法:
File file = new File ("test.txt");
FileInputStream in=new FileInputStream(file);
BufferedInputStream inBuffered=new BufferedInputStream (in);
这里的
BufferedInputStream主要是提供了缓存机制,先读入一个byte[],等count到达缓存Byte[]的大小的时候,再一次读入。你也可以写成:
BufferedInputStream inBuffered = new BufferedInputStream (new FileInputStream(new File ("test.txt")));
从使用者的角度来看装饰者模式,可以看出它的一个缺点:装饰者模式的实现对于使用者是透明的。