装饰者模式

装饰者设计模式UML类图

装饰者模式UML类图

装饰者设计模式java代码实现

/** 
* Interface for trolls.
巨魔
*/
public interface Troll {

void attack();

int getAttackPower();

void fleeBattle();

}

/**
* SimpleTroll implements {@link Troll} interface directly.
*/
public class SimpleTroll implements Troll {

private static final Logger LOGGER = LoggerFactory.getLogger(SimpleTroll.class);

@Override
public void attack() {
LOGGER.info("The troll tries to grab you!");
}

@Override
public int getAttackPower() {
return 10;
}

@Override
public void fleeBattle() {
//巨魔惊恐地尖叫着逃跑了!
LOGGER.info("The troll shrieks in horror and runs away!");
}
}

/**
* Decorator that adds a club for the troll.
为巨魔添加棒子的装饰器。
*/
public class ClubbedTroll implements Troll {

private static final Logger LOGGER = LoggerFactory.getLogger(ClubbedTroll.class);

private Troll decorated;

public ClubbedTroll(Troll decorated) {
this.decorated = decorated;
}

@Override
public void attack() {
decorated.attack();
//巨魔用棍子打你!
LOGGER.info("The troll swings at you with a club!");
}

@Override
public int getAttackPower() {
return decorated.getAttackPower() + 10;
}

@Override
public void fleeBattle() {
//逃离战斗
decorated.fleeBattle();
}
}

/**
* The Decorator pattern is a more flexible alternative to subclassing. The Decorator class
* implements the same interface as the target and uses composition to "decorate" calls to the
* target. Using the Decorator pattern it is possible to change the behavior of the class during
* runtime.
Decorator模式是子类的一种更灵活的替代方法。 Decorator类实现与目标相同的接口,并使用组合“修饰”对目标的调用。 使用Decorator模式,可以在运行时更改类的行为。
*
* <p>In this example we show how the simple {@link SimpleTroll} first attacks and then flees the
* battle. Then we decorate the {@link SimpleTroll} with a {@link ClubbedTroll} and perform the
* attack again. You can see how the behavior changes after the decoration.
<p>在此示例中,我们显示了简单的{@link SimpleTroll}如何首先攻击然后逃离战斗。 然后,我们用{@link ClubbedTroll}装饰{@link SimpleTroll},然后再次执行攻击。 您可以看到装饰后行为如何变化。
*/
public class App {

private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

/**
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {

// simple troll
LOGGER.info("A simple looking troll approaches.");
var troll = new SimpleTroll();
troll.attack();
troll.fleeBattle();
LOGGER.info("Simple troll power {}.\n", troll.getAttackPower());

// change the behavior of the simple troll by adding a decorator
LOGGER.info("A troll with huge club surprises you.");
var clubbedTroll = new ClubbedTroll(troll);
clubbedTroll.attack();
clubbedTroll.fleeBattle();
LOGGER.info("Clubbed troll power {}.\n", clubbedTroll.getAttackPower());
}
}

应用场景

Java I/O中的装饰者模式

使用 Java I/O 的时候总是有各种输入流、输出流、字符流、字节流、过滤流、缓冲流等等各种各样的流,不熟悉里边的设计模式的话总会看得云里雾里的,现在通过设计模式的角度来看 Java I/O,会好理解很多。
先用一幅图来看看Java I/O到底是什么,下面的这幅图生动的刻画了Java I/O的作用。
java-io-flow
由上图可知在Java中应用程序通过输入流(InputStream)的Read方法从源地址处读取字节,然后通过输出流(OutputStream)的Write方法将流写入到目的地址。
流的来源主要有三种:本地的文件(File)、控制台、通过socket实现的网络通信
下面的图可以看出Java中的装饰者类和被装饰者类以及它们之间的关系,这里只列出了InputStream中的关系:
InputStream
由上图可以看出只要继承了FilterInputStream的类就是装饰者类,可以用于包装其他的流,装饰者类还可以对装饰者和类进行再包装。
这里总结几种常用流的应用场景:

流名称 应用场景
ByteArrayInputStream 访问数组,把内存中的一个缓冲区作为 InputStream 使用,CPU从缓存区读取数据比从存储介质的速率快10倍以上
StringBufferInputStream 把一个 String 对象作为。InputStream。不建议使用,在转换字符的问题上有缺陷
FileInputStream 访问文件,把一个文件作为 InputStream ,实现对文件的读取操作
PipedInputStream 访问管道,主要在线程中使用,一个线程通过管道输出流发送数据,而另一个线程通过管道输入流读取数据,这样可实现两个线程间的通讯
SequenceInputStream 把多个 InputStream 合并为一个 InputStream . 序列输入流类允许应用程序把几个输入流连续地合并起来
DataInputStream 特殊流,读各种基本类型数据,如byte、int、String的功能
ObjectInputStream 对象流,读对象的功能
PushBackInputStream 推回输入流,可以把读取进来的某些数据重新回退到输入流的缓冲区之中
BufferedInputStream 缓冲流,增加了缓冲功能
public class StreamDemo {
public static void main(String[] args) throws IOException{
DataInputStream in=new DataInputStream(new BufferedInputStream(new FileInputStream("D:\\hello.txt")));
while(in.available()!=0) {
System.out.print((char)in.readByte());
}
in.close();
}
}

整个Java IO体系都是基于字符流(InputStream/OutputStream) 和 字节流(Reader/Writer)作为基类,下面画出OutputStream、Reader、Writer的部分类图
OutputStream
Reader
Writer

spring cache 中的装饰者模式

看 org.springframework.cache.transaction 包下的 TransactionAwareCacheDecorator 这个类

public class TransactionAwareCacheDecorator implements Cache {
private final Cache targetCache;

public TransactionAwareCacheDecorator(Cache targetCache) {
Assert.notNull(targetCache, "Target Cache must not be null");
this.targetCache = targetCache;
}

public <T> T get(Object key, Class<T> type) {
return this.targetCache.get(key, type);
}

public void put(final Object key, final Object value) {
// 判断是否开启了事务
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// 将操作注册到 afterCommit 阶段
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
public void afterCommit() {
TransactionAwareCacheDecorator.this.targetCache.put(key, value);
}
});
} else {
this.targetCache.put(key, value);
}
}
// ...省略...
}

该类实现了 Cache 接口,同时将 Cache 组合到类中成为了成员属性 targetCache,所以可以大胆猜测 TransactionAwareCacheDecorator 是一个装饰类,不过这里并没有抽象装饰类,且 TransactionAwareCacheDecorator 没有子类,这里的装饰类关系并没有Java I/O 中的装饰关系那么复杂
SpringCache
该类的主要功能:通过 Spring 的 TransactionSynchronizationManager 将其 put/evict/clear 操作与 Spring 管理的事务同步,仅在成功的事务的 after-commit 阶段执行实际的缓存 put/evict/clear 操作。如果没有事务是 active 的,将立即执行 put/evict/clear 操作

spring session 中的装饰者模式

类 ServletRequestWrapper 的代码如下:

public class ServletRequestWrapper implements ServletRequest {
private ServletRequest request;

public ServletRequestWrapper(ServletRequest request) {
if (request == null) {
throw new IllegalArgumentException("Request cannot be null");
}
this.request = request;
}

@Override
public Object getAttribute(String name) {
return this.request.getAttribute(name);
}
//...省略...
}

可以看到该类对 ServletRequest 进行了包装,这里是一个装饰者模式,再看下图,spring session 中 SessionRepositoryFilter 的一个内部类 SessionRepositoryRequestWrapper 与 ServletRequestWrapper 的关系
HttpServletRequestWrapper
可见 ServletRequestWrapper 是第一层包装,HttpServletRequestWrapper 通过继承进行包装,增加了 HTTP 相关的功能,SessionRepositoryRequestWrapper 又通过继承进行包装,增加了 Session 相关的功能

Mybatis 缓存中的装饰者模式

org.apache.ibatis.cache 包的文件结构如下所示
MybatisCache
我们通过类所在的包名即可判断出该类的角色,Cache 为抽象构件类,PerpetualCache 为具体构件类,decorators 包下的类为装饰类,没有抽象装饰类
通过名称也可以判断出装饰类所要装饰的功能