我的理解,就是一种组合
package com.test.pattern.decorator; //组件对象的接口,可以给这些对象动态地添加职责 abstract class Component { public abstract void operation(); } //具体组件 class ConcreteComponent extends Component { @Override public void operation() { } } //装饰器接口,维持一个指向组件对象的接口对象, 并定义一个与组件接口一致的接口 abstract class Decorator extends Component { /** 持有组件对象 */ protected Component component; public Decorator(Component component) { this.component = component; } public void operation() { component.operation(); } } //装饰器的具体实现对象 class ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component component) { super(component); } private void operationFirst() {} private void operationLast() {} public void operation() { operationFirst(); super.operation(); operationLast(); } } class ConcreteDecoratorB extends Decorator { public ConcreteDecoratorB(Component component) { super(component); } private void operationFirst() {} private void operationLast() {} public void operation() { operationFirst(); super.operation(); operationLast(); } } public class Client { public static void main(String[] args) { Component c1 = new ConcreteComponent(); Decorator decoratorA = new ConcreteDecoratorA(c1); //给对象透明的增加功能A并调用 decoratorA.operation(); Decorator decoratorB = new ConcreteDecoratorB(c1); //给对象透明的增加功能B并调用 decoratorB.operation(); Decorator decoratorBandA = new ConcreteDecoratorB(decoratorA);//装饰器也可以装饰具体的装饰对象,此时相当于给对象在增加A的功能基础上在添加功能B decoratorBandA.operation(); } }
======================================================================================================================================================
使用spring-data-redis替代mybatis默认二级缓存的时候,实际上就用到了装饰器模式,因为mytatis的缓存LoggingCache就是一种装饰器的设计。来看看怎么替换mybatis的默认二级缓存:
一般来说可以实现Cache接口,但是采用装饰器设计模式的LoggingCache,才是我今天想要学习的,下面的代码只截取了重点
/*** Eclipse Class Decompiler plugin, copyright (c) 2016 Chen Chao (cnfree2000@hotmail.com) ***/ package org.apache.ibatis.cache.decorators; import java.util.concurrent.locks.ReadWriteLock; import org.apache.ibatis.cache.Cache; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; public class LoggingCache implements Cache { private Log log; private Cache delegate; protected int requests = 0; protected int hits = 0; public LoggingCache(Cache delegate) { this.delegate = delegate; this.log = LogFactory.getLog(getId()); } 略 }
重点就是这个构造函数
当我们继承它的时候,就可以使用自己传入的Cache实现:
public class LoggingRedisCache extends LoggingCache { public LoggingRedisCache(String id) { super(new MybatisRedisCache(id)); } }
其中MybatisRedisCache是我们自己定义的,Cache的实现类
======================================================================================================================================================
JAVA IO 实现就使用了装饰器模式,通过对JAVA IO 的深入了解,我突然觉得真正明白什么叫装饰器模式了,这里的装饰二字的含义。所谓装饰,就是子类不断的装饰自己的父类,通过装饰,一层一层,加添自己新的功能。
InputStream是一个抽象类,JAVA IO可以分为两大类,一类是节点流,一类是过滤流。所谓节点流,就是直接操作数据源,比如FileInputStream和FileOutputStream就是节点流,他们直接操作文件;所谓过滤流,就是不直接操作文件,而是操作他们自己所持有的节点流,比如BufferedInputStream,ButfferedOutputStream,他们在自己内部分别持有了InputStream和OutputStream的引用对象,他们可以引用任何节点流,然后比如BufferedInputStream在read的时候,其实就是调用的inputStream的read(比如持有的这个对象实际引用的是FileInputStream,就是调用的FileinputStream的read),只不过在调用持有对象的read之前,先判断够不够buffer.length这么多,够了再一次读,避免频繁的读操作。实际上装饰器模式是很简单的,只是我之前一直看网上的代码,看的晕乎乎的,还是要看实际框架中的用法,才能有所收获
。
哦 对了忘了说,区分节点流和过滤流最直接的方法,就是所有继承了FilterInoputStream/FilterOutputStream的都是过滤流
另外还要说一些关闭过滤流,比如关闭ButfferedOutputStream的方式,看了它的源代码,它的close方法实际上包含两步,第一先flush一下,把缓存buffer里面的先写磁盘,第二部就直接把持有的outputstream给关闭,所以当我们使用ButfferedOutputStream的时候,是不需要手动关闭outputstream的,直接关闭ButfferedOutputStream就可以了