前言
本文主要内容
- 介绍装饰模式
- 介绍装饰模式的通用类图及通用代码
- 简单使用装饰模式
正文
介绍装饰模式
定义:
装饰模式(Decorator Pattern)是一种比较常见的模式,其定义如下:Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。) -设计模式之禅 第2版
我的理解是,装饰模式就是"动态"地增加完成一个行为所需要的步骤.
简单使用装饰模式
举例来讲,写一个日志类,来记录一些操作信息和异常信息.最初的版本,打印出信息就好了.后来在生产阶段,将日志信息保存在文件中(这个配置文件就好了).对于info,直接记录就好了,但是对于severe,需要及处理,发个邮件好了.这就是将日志信息的输出增加了一个过程.对于这个处理,可以有多个方法
最简单直接的,直接写入那个日志处理类里就好了.写入前/写入后发送.但是这个相当不灵活,比如,对某些不需要及时处理的错误(比如要放弃的功能),我不想让它发邮件骚扰我.这样直接写入的方法就无法适用了.
使用继承?想来是相当不错的.可以增加发邮件的步骤,也可以不增加.但是,这也有问题,发现异常后,仅凭异常信息无法锁定位置,需要参考下info.但是日志文件可读性不佳/由于长时间未处理,服务器上的info已经被覆盖了.基于此,我想将日志信息保存在数据库中,所以,再增加一个保存数据库的方法.这样的话,还得再来一个子类.数据库保存+发送邮件.若,我又不想接受邮件的骚扰了,就只用保存在数据库中就好了.这样就得再来一个子类.不够灵活
之后,就是装饰类的方法了.将这个几个步骤分割开.邮件是一个类,保存在数据库中是一个类.然后根据需要进行组装就好了
类图如下

IMyLog抽象出日志类的方法,MyLog为其实现类.
public interface IMyLog {public void getInfo(String msg);public void getSevere(String msg);}
@AllArgsConstructorpublic class MyLog implements IMyLog {private Logger logger;@Overridepublic void getInfo(String msg) {logger.info(msg);}@Overridepublic void getSevere(String msg) {logger.info(msg);}}
MyLogDecorator为装饰器类
@AllArgsConstructorpublic abstract class AbstractMyLogDecorator implements IMyLog {private IMyLog myLog;@Overridepublic void getInfo(String msg) {this.myLog.getInfo(msg);}@Overridepublic void getSevere(String msg) {this.myLog.getSevere(msg);}}
public class SaveMyLogDecorator extends AbstractMyLogDecorator{public SaveMyLogDecorator(IMyLog myLog) {super(myLog);}private void save(String msg){System.out.println("保存信息到数据库中,信息为:" + msg);}@Overridepublic void getInfo(String msg) {save(msg);super.getInfo(msg);}@Overridepublic void getSevere(String msg) {save(msg);super.getSevere(msg);}}
public class SendMyLogDecorator extends AbstractMyLogDecorator {public SendMyLogDecorator(IMyLog myLog) {super(myLog);}private void sendMail(String msg){System.out.println("已经发送邮件,信息为:" + msg);}@Overridepublic void getSevere(String msg) {sendMail(msg);super.getSevere(msg);}}
public class Client {public static void main(String[] args) {IMyLog myLog = new MyLog(Logger.getLogger("log"));myLog = new SendMyLogDecorator(myLog);myLog = new SaveMyLogDecorator(myLog);myLog.getSevere("Hello World");}}
装饰模式在IO中的应用
java中的IO流可以简单的分为两类,节点流和处理流。节点流就是节点到节点间的信息传输,这些节点可以是文件、管道也可以是一个数组;传输的信息可以是字节也可以是字符。处理流就是为这个信息传输过程增加点其他功能,比如缓存,类型转换。这个增加的方式,就是使用装饰器模式。以ByteArrayInputStream 和 BufferedInputStream来举例说明。ByteArrayInputStream 为节点流,BufferedInputStream 为处理流,增加了缓存功能。来看一下类图
再看一下关键代码 read,从节点读取数据

进入 fill 填充缓存
将上图关键代码放大如下
该过程调用了如下方法,其中 in 为 InputStream 。也就是调用节点流读取数据存入缓存中
