和凡科网类似的网站,哈尔滨网站推广,国外单栏wordpress,专业网站设计公司排名2. 装饰器设计模式
2.1 实现原理
装饰器设计模式#xff08;Decorator#xff09;是一种结构型设计模式#xff0c;它允许动态地为对象添加新的行为。它通过创建一个包装器来实现#xff0c;先将对象放入一个装饰器类中#xff0c;再将装饰器类放入另一个装饰器类中Decorator是一种结构型设计模式它允许动态地为对象添加新的行为。它通过创建一个包装器来实现先将对象放入一个装饰器类中再将装饰器类放入另一个装饰器类中以此类推形成一条包装链。这样可以在不改变原有对象的情况下动态地添加新的行为或修改原有行为。
在 Java 中实现装饰器设计模式的步骤如下
1定义一个接口或抽象类作为被装饰对象的基类
/*** 接口描述装饰接口** Author crysw* Version 1.0* Date 2023/12/14 21:15*/
public interface Component {/*** 用于定义被装饰对象的基本行为*/void operation();
}2定义一个具体的被装饰对象实现基类中的方法
/*** 类描述具体的被装饰对象类** Author crysw* Version 1.0* Date 2023/12/14 21:17*/
Slf4j
public class ConcreteComponent implements Component {Overridepublic void operation() {log.info(ConcreteComponent is doing something...);}
}3定义一个抽象装饰器类继承基类并将被装饰对象作为属性
/*** 类描述装饰器类** Author crysw* Version 1.0* Date 2023/12/14 21:19*/
public abstract class Decorator implements Component {protected Component component;public Decorator(Component component) {this.component component;}Overridepublic void operation() {this.component.operation();}
}4定义具体的装饰器类继承抽象装饰器类并实现增强逻辑。
/*** 类描述具体的装饰器类** Author crysw* Version 1.0* Date 2023/12/14 21:21*/
Slf4j
public class ConcreteDecoratorA extends Decorator {public ConcreteDecoratorA(Component component) {super(component);}/*** 重写方法并做增强*/Overridepublic void operation() {// 我们先调用被装饰对象的同名方法然后添加新的行为super.operation();log.info(ConcreteDecoratorA is adding new behavior...);}
}5使用装饰器增强被装饰对象
/*** 类描述装饰器设计模式测试** Author crysw* Version 1.0* Date 2023/12/14 21:25*/
Slf4j
public class DecoratePatternTest {Testpublic void test() {Component component new ConcreteComponent();ConcreteDecoratorA concreteDecoratorA new ConcreteDecoratorA(component);concreteDecoratorA.operation();}
}测试效果 ConcreteComponent is doing something...增强ConcreteDecoratorA is adding new behavior...虽然装饰器模式和静态代理模式有一些相似之处但它们之间还是有区别的
代理模式的目的是为了控制对对象的访问它在对象的外部提供一个代理对象来控制对原始对象的访问。代理对象和原始对象通常实现同一个接口或继承同一个类以保证二者可以互相替代。
装饰器模式的目的是为了动态地增强对象的功能它在对象的内部通过一种包装器的方式来实现。装饰器模式中装饰器类和被装饰对象通常实现同一个接口或继承同一个类以保证二者可以互相替代。装饰器模式也被称为包装器模式。 需要注意装饰器模式虽然可以实现动态地为对象增加行为但是会增加系统的复杂性因此在使用时需要仔细权衡利弊。 2.2 使用场景
2.2.1 从IO库的设计理解装饰器
InputStream 是一个抽象类FileInputStream 是专门用来读取文件流的子类。BufferedInputStream 是一个支持带缓存功能的数据读取类可以提高数据读取的效率具体的代码如下
InputStream in new FileInputStream(D:/test.txt);
InputStream bin new BufferedInputStream(in);
byte[] data new byte[128];while (bin.read(data) ! -1) {//...
}上面代码中先创建一个FileInputStream 对象然后再传递给 BufferedInputStream 对象来使用。Java IO 为什么不设计一个继承 FileInputStream 并且支持缓存的BufferedFileInputStream 类呢比如
// 实际IO没有这样设计
InputStream bin new BufferedFileInputStream(/user/wangzheng/test.txt);
byte[] data new byte[128];
while (bin.read(data) ! -1) {//...
}1基于继承的设计方案 如果 InputStream 只有一个子类 FileInputStream 的话那我们在 FileInputStream基础之上再设计一个孙子类 BufferedFileInputStream也算是可以接受的毕竟继承结构还算简单。但实际上继承 InputStream 的子类有很多。我们需要给每一个 InputStream 的子类再继续派生支持缓存读取的孙子类。
除了支持缓存读取之外如果我们还需要对功能进行其他方面的增强比如下面的DataInputStream 类支持按照基本数据类型int、boolean、long 等来读取数据。
FileInputStream in new FileInputStream(/user/wangzheng/test.txt);
DataInputStream din new DataInputStream(in);
int data din.readInt();在这种情况下如果我们继续按照继承的方式来实现的话就需要再继续派生出DataFileInputStream、DataPipedInputStream 等类。如果我们还需要既支持缓存、又支持按照基本类型读取数据的类那就要再继续派生出BufferedDataFileInputStream、BufferedDataPipedInputStream 等 n 多类。这还只是附加了两个增强功能如果我们需要附加更多的增强功能那就会导类继承结构变得无比复杂代码既不好扩展也不好维护。
2基于装饰器模式的设计方案 讲到“组合优于继承”可以使用组合来替代继承。针对刚刚的继承结构过于复杂的问题我们可以通过将继承关系改为组合关系来解决。下面的代码展示了 Java IO 的这种设计思路。
public abstract class InputStream {//...public int read(byte b[]) throws IOException {return read(b, 0, b.length);}public int read(byte b[], int off, int len) throws IOException {//...}public long skip(long n) throws IOException {//...}public int available() throws IOException {return 0;}public void close() throws IOException {}public synchronized void mark(int readlimit) {}public synchronized void reset() throws IOException {throw new IOException(mark/reset not supported);}public boolean markSupported() {return false;}
}public class BufferedInputStream extends InputStream {protected volatile InputStream in;// 包装InputStream对象protected BufferedInputStream(InputStream in) {this.in in;}//...实现基于缓存的读数据接口...
}
public class DataInputStream extends InputStream {protected volatile InputStream in;// 包装InputStream对象protected DataInputStream(InputStream in) {this.in in;}//...实现读取基本类型数据的接口
}装饰器类和原始类继承同样的父类这样我们可以对原始类“嵌套”多个装饰器类。 装饰器类是对功能的增强。 2.2.2 mybatis的缓存设计
org.apache.ibatis.builder.MapperBuilderAssistant#useNewCache创建缓存的过程
public class MapperBuilderAssistant extends BaseBuilder {// 。。。public Cache useNewCache(Class? extends Cache typeClass,Class? extends Cache evictionClass,Long flushInterval,Integer size,boolean readWrite,boolean blocking,Properties props) {// 根据类型生成实例并进行配置Cache cache new CacheBuilder(currentNamespace).implementation(valueOrDefault(typeClass, PerpetualCache.class)).addDecorator(valueOrDefault(evictionClass, LruCache.class)).clearInterval(flushInterval).size(size).readWrite(readWrite).blocking(blocking).properties(props).build();configuration.addCache(cache);currentCache cache;return cache;}
}默认的缓存如下本质就是维护了一个简单的HashMap
public class PerpetualCache implements Cache {private final String id;private MapObject, Object cache new HashMapObject, Object();public PerpetualCache(String id) {this.id id;}Overridepublic String getId() {return id;}Overridepublic int getSize() {return cache.size();}Overridepublic void putObject(Object key, Object value) {cache.put(key, value);}Overridepublic Object getObject(Object key) {return cache.get(key);}// ...省略其他的简单的方法
}缓存的实现当然不可能这么简单事实上他的构造过程org.apache.ibatis.mapping.CacheBuilder#build如下
public class CacheBuilder {private final String id;private Class? extends Cache implementation;private final ListClass? extends Cache decorators;public Cache build() {// 设置默认的cache实现并绑定默认的淘汰策略setDefaultImplementations();// 利用反射创建实例Cache cache newBaseCacheInstance(implementation, id);// 设置properties属性setCacheProperties(cache);// issue #352, do not apply decorators to custom caches// 自定义缓存需要自己实现对应的特性如淘汰策略等// 通常情况自定义缓存有自己的独立配置如redis、ehcacheif (PerpetualCache.class.equals(cache.getClass())) {for (Class? extends Cache decorator : decorators) {cache newCacheDecoratorInstance(decorator, cache);setCacheProperties(cache);}// 这是标准的装饰器这里使用了装饰器设计模式cache setStandardDecorators(cache);} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {cache new LoggingCache(cache);}return cache;}
}mybatis会使用装饰者设计模式对默认cache进行装饰使其具有LRU的能力如下
private void setDefaultImplementations() {if (implementation null) {implementation PerpetualCache.class;if (decorators.isEmpty()) {decorators.add(LruCache.class);}}
}最后使用其他的装饰器对cache进行装饰使其就有更多的能力.
private Cache setStandardDecorators(Cache cache) {// 包装增强cachetry {MetaObject metaCache SystemMetaObject.forObject(cache);if (size ! null metaCache.hasSetter(size)) {metaCache.setValue(size, size);}if (clearInterval ! null) {cache new ScheduledCache(cache);((ScheduledCache) cache).setClearInterval(clearInterval);}if (readWrite) {cache new SerializedCache(cache);}cache new LoggingCache(cache);cache new SynchronizedCache(cache);if (blocking) {cache new BlockingCache(cache);}return cache;} catch (Exception e) {throw new CacheException(Error building standard cache decorators. Cause: e, e);}
}