设计模式系列之装饰模式(Decorator Pattern)——扩展系统功能 (2)

透明装饰模式,客户端完全针对抽象编程,装饰模式的透明性要求客户端程序不应该将对象声明为具体构件类型或具体装饰类型,而应该全部声明为抽象构件类型。对于客户端而言,具体构件对象和具体装饰对象没有任何区别。也就是应该使用如下代码:

// 透明装饰模式应当使用抽象构件类型定义对象 Component c, c1; c = new ConcreteComponent(); c1 = new ConcreteDecorator(c); // **注意:透明装饰模式不应该使用具体类型来声明对象,比如下面的代码** // ConcreteComponent c = new ConcreteComponent(); // ConcreteDecorator c1 = new ConcreteDecorator(c);

但是在实际使用过程中,由于新增行为可能需要单独调用(即客户端想自己单独调用装饰类中新增的方法来控制是否以及如何增强),因此这种形式的装饰模式也经常出现,这种装饰模式被称为半透明(Semi-transparent)装饰模式

半透明装饰模式中的具体装饰类对应的实现,即变为如下:

public class ConcreteDecorator extends Decorator { public ConcreteDecorator(Component component) { super(component); } //@Override //public void operation() { // 调用原有业务方法 // super.operation(); // 调用新增业务方法 //addedBehavior(); //} // 新增业务方法 public void addedBehavior() { } }

注意 半透明装饰模式中的ConcreteDecorator类继承了父装饰类Decorator的operation()方法但并没有重写operation()方法,并新增了业务方法addedBehavior(),但这两个方法是完全独立的,没有任何调用关系。

即 半透明装饰模式中的ConcreteDecorator中的operation()并没有对注入的Component进行增强,只是增加了额外的方法addedBehavior(),这样一来是否增强Component就取决于客户端是否调用addedBehavior()方法。

客户端想增强Component,就需要分别调用这两个方法,代码片段如下:

// 原有构件 Component component = new ConcreteComponent(); // 用装饰器包装原有构件 ConcreteDecorator enhancedComponent = new ConcreteDecorator(component); // 调用原有业务方法 enhancedComponent.operation(); // 调用新增业务方法,从而实现增强 enhancedComponent.addedBehavior();

显然半透明装饰模式最大的缺点在于不能实现对同一个对象的多次装饰,而且客户端需要有区别地对待装饰之前的对象和装饰之后的对象。

模式应用 模式在JDK中的应用

Java中的java.io包下io流的设计就充分使用了装饰模式。举个具体的例子,BufferedInputStream就是一个具体装饰者,它能为一个原本没有缓冲(buffer)功能的InputStream增加缓冲的功能。下面的代码应该司空见惯。

BufferedInputStream reader = new BufferedInputStream(new FileInputStream(new File("/tmp/1.txt")));

FileInputStream本没有缓冲功能,每次调用read方法,都会发起系统调用读数据。用BufferedInputStream来装饰它,那么每次调用read方法,会向操作系统多读一定量数据进入内存的buf[],这样就提高了读的效率,避免频繁发起系统调用。

BufferedInputStream构造器中注入了InputStream

public BufferedInputStream(InputStream in, int size) { super(in); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; }

BufferedInputStream继承了父装饰器FilterInputStream,维持了对InputStream的引用

public class FilterInputStream extends InputStream { protected volatile InputStream in; protected FilterInputStream(InputStream in) { this.in = in; } }

BufferedInputStream重写了read()从而实现对引用的InputStream增强。有兴趣可以读读相关源码。

模式在开源项目中的应用

mybatic中org.apache.ibatis.session.Configuration#newExecutor有这样一段代码

public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsffzx.html