【设计模式】组件代理简例,与外源代码解耦
问题背景
开发过程中,我们会引用其他外源组件进行业务代码的开发。由于外源组件的迭代更新与我们的业务代码不一致,当升级或回退外源组件时可能会遇到组件的接口变更的问题,为了使得咱们的业务代码尽量少受它的影响,采用代理模式使得接口分离;
涉及的技术点有:代理模式、桥接模式、享元模式、简单工厂模式。
现在我们就以本地缓存组件guava-cache为例讲解一下具体的实现方式;
创建接口
创建自定义接口继承组件接口可以在该接口中定义一些自己的业务方法,代码如下
import com.google.common.cache.Cache;
/**
* description:
*
* @author : niudongjun
* @date : 2022/5/29 17:15
*/
public interface CacheOperate<K, V> extends Cache<Comparable<K>, V> {
}
自定义接口是分离组件接口的关键一步。
创建接口代理
采用JDK的代理方式去创建接口的代理,也可以采用其他方式,代码如下
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
/**
* description:
*
* @author : niudongjun
* @date : 2022/5/29 17:20
*/
public class CacheProxy<K, V> implements InvocationHandler {
private static final Map<String, Cache<Comparable<?>, ?>> CACHE_INSTANCE_MAP = new ConcurrentHashMap<>();
private final String name;
private final Map<String, String> conf;
protected CacheOperate<K, V> cacheOperate;
public CacheProxy(String name) {
this(name, null);
}
public CacheProxy(String name, Map<String, String> conf) {
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("Cache name invalid");
}
this.name = name;
this.conf = conf;
this.cacheOperate = (CacheOperate<K, V>) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class<?>[]{CacheOperate.class}, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(obtainCache(), args);
}
private static final Pattern NUMBER_PATTERN = Pattern.compile("\\d+");
private Cache<Comparable<?>, ?> obtainCache() {
Cache<Comparable<?>, ?> cache = CACHE_INSTANCE_MAP.get(name);
if (Objects.isNull(cache)) {
synchronized (CACHE_INSTANCE_MAP) {
CacheBuilder<Object, Object> build = CacheBuilder.newBuilder();
if (MapUtils.isNotEmpty(conf)) {
String maxSize = conf.get("maxSize");
String expireAfterAccess = conf.get("expireAfterAccess");
String expireAfterWrite = conf.get("expireAfterWrite");
if (Objects.nonNull(maxSize) && NUMBER_PATTERN.matcher(maxSize).matches()) {
build.maximumSize(Long.parseLong(maxSize));
} else {
build.maximumSize(1000);
}
if (Objects.nonNull(expireAfterAccess) && NUMBER_PATTERN.matcher(expireAfterAccess).matches()) {
build.expireAfterAccess(Long.parseLong(expireAfterAccess), TimeUnit.MILLISECONDS);
} else {
build.expireAfterAccess(1, TimeUnit.SECONDS);
}
if (Objects.nonNull(expireAfterWrite) && NUMBER_PATTERN.matcher(expireAfterWrite).matches()) {
build.expireAfterWrite(Long.parseLong(expireAfterWrite), TimeUnit.MILLISECONDS);
} else {
build.expireAfterWrite(2, TimeUnit.SECONDS);
}
}
Cache<Comparable<?>, ?> comparableCache = build.build();
CACHE_INSTANCE_MAP.put(name, comparableCache);
return comparableCache;
}
} else {
return cache;
}
}
}
创建一个简单的业务实现
创建一个实现类继承代理类,其中实现一些业务方法,代码如下
public class SimpleCache extends CacheProxy<String, String> {
public SimpleCache(String name) {
super(name);
}
public void put(String key, String value) {
this.cacheOperate.put(key, value);
}
public String get(String key) {
return this.cacheOperate.getIfPresent(key);
}
public void remove(String key) {
this.cacheOperate.invalidate(key);
}
}
这里必须使得泛型与实际类型绑定,如果还想使用泛型建议采用序列化方法
创建组件的工厂类
该步骤主要是因为有些组件需要注入一些复杂的配置,为了简单使用,我们通常会创建工厂,把复杂的配置用简单的方式去实现,这里只是一个简单的例子,代码如下
import org.apache.commons.lang3.StringUtils;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* description:
*
* @author : niudongjun
* @date : 2022/5/29 17:56
*/
public class CacheFactory {
private static final Map<String, CacheProxy> CACHE_INSTANCE_MAP = new ConcurrentHashMap<>();
public static SimpleCache getSimpleCache(String name) {
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("");
}
SimpleCache cacheProxy = (SimpleCache) CACHE_INSTANCE_MAP.get(name);
if (Objects.isNull(cacheProxy)) {
synchronized (CACHE_INSTANCE_MAP) {
SimpleCache simpleCache = new SimpleCache(name);
CACHE_INSTANCE_MAP.put(name, simpleCache);
return simpleCache;
}
}
return cacheProxy;
}
}
总结
设计模式是代码创作者经过多次的业务洗礼所总结出的切实可行的高效方案,对于设计模式的学习是面向对象编程思想不可或缺的一环,还是得好好学习下。以后的业务代码开发过程中要多多思考联系设计模式去解决问题。