前言

本文主要内容

  • 介绍装饰模式
  • 介绍装饰模式的通用类图及通用代码
  • 简单使用装饰模式

正文

介绍装饰模式

定义:

装饰模式(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已经被覆盖了.基于此,我想将日志信息保存在数据库中,所以,再增加一个保存数据库的方法.这样的话,还得再来一个子类.数据库保存+发送邮件.若,我又不想接受邮件的骚扰了,就只用保存在数据库中就好了.这样就得再来一个子类.不够灵活

之后,就是装饰类的方法了.将这个几个步骤分割开.邮件是一个类,保存在数据库中是一个类.然后根据需要进行组装就好了

类图如下

装饰模式 - 图1

IMyLog抽象出日志类的方法,MyLog为其实现类.

  1. public interface IMyLog {
  2. public void getInfo(String msg);
  3. public void getSevere(String msg);
  4. }
  1. @AllArgsConstructor
  2. public class MyLog implements IMyLog {
  3. private Logger logger;
  4. @Override
  5. public void getInfo(String msg) {
  6. logger.info(msg);
  7. }
  8. @Override
  9. public void getSevere(String msg) {
  10. logger.info(msg);
  11. }
  12. }

MyLogDecorator为装饰器类

  1. @AllArgsConstructor
  2. public abstract class AbstractMyLogDecorator implements IMyLog {
  3. private IMyLog myLog;
  4. @Override
  5. public void getInfo(String msg) {
  6. this.myLog.getInfo(msg);
  7. }
  8. @Override
  9. public void getSevere(String msg) {
  10. this.myLog.getSevere(msg);
  11. }
  12. }
  1. public class SaveMyLogDecorator extends AbstractMyLogDecorator{
  2. public SaveMyLogDecorator(IMyLog myLog) {
  3. super(myLog);
  4. }
  5. private void save(String msg){
  6. System.out.println("保存信息到数据库中,信息为:" + msg);
  7. }
  8. @Override
  9. public void getInfo(String msg) {
  10. save(msg);
  11. super.getInfo(msg);
  12. }
  13. @Override
  14. public void getSevere(String msg) {
  15. save(msg);
  16. super.getSevere(msg);
  17. }
  18. }
  1. public class SendMyLogDecorator extends AbstractMyLogDecorator {
  2. public SendMyLogDecorator(IMyLog myLog) {
  3. super(myLog);
  4. }
  5. private void sendMail(String msg){
  6. System.out.println("已经发送邮件,信息为:" + msg);
  7. }
  8. @Override
  9. public void getSevere(String msg) {
  10. sendMail(msg);
  11. super.getSevere(msg);
  12. }
  13. }
  1. public class Client {
  2. public static void main(String[] args) {
  3. IMyLog myLog = new MyLog(Logger.getLogger("log"));
  4. myLog = new SendMyLogDecorator(myLog);
  5. myLog = new SaveMyLogDecorator(myLog);
  6. myLog.getSevere("Hello World");
  7. }
  8. }

装饰模式在IO中的应用

java中的IO流可以简单的分为两类,节点流和处理流。节点流就是节点到节点间的信息传输,这些节点可以是文件、管道也可以是一个数组;传输的信息可以是字节也可以是字符。处理流就是为这个信息传输过程增加点其他功能,比如缓存,类型转换。这个增加的方式,就是使用装饰器模式。以ByteArrayInputStream 和 BufferedInputStream来举例说明。ByteArrayInputStream 为节点流,BufferedInputStream 为处理流,增加了缓存功能。来看一下类图 装饰模式 - 图2再看一下关键代码 read,从节点读取数据
图片.png
进入 fill 填充缓存
图片.png
将上图关键代码放大如下
图片.png
该过程调用了如下方法,其中 in 为 InputStream 。也就是调用节点流读取数据存入缓存中
图片.png