创建型-抽象工厂模式 Link to heading
抽象工厂模式也可以叫其他工厂模式,它可以在抽象工厂中创建出其他工厂,与工厂模式一样,都是用来解决接口选择的问题。
场景模拟 Link to heading
此前业务使用的数据库为单机Redis
但是随着业务的发展,此前的单机Redis已经无法适用于项目,所以需要升级成健壮的Redis集群服务。
- 很多服务都用到Redis,需要一起升级。
- 需要同时兼容集群EGM和IIR
- 两套集群的接口不一样,需要同时做兼容
- 不能影响目前正常运行的系统
最初设计 Link to heading
- 给接口添加Redis集群使用类型,以控制使用哪套集群服务。
- 但是不能停止已经使用的集群,所以还保留了此前单机的Redis接口。
工程结构
├─application
│ ├─CacheService.java
│ └─CacheServiceImpl.java
└─redis
├─RedisUtils.java
└─cluster
├─EGM.java
└─IIR.java
服务定义
public class EGM {
private Map<String, String> dataMap = new ConcurrentHashMap<>();
public String gain(String key) {
return dataMap.get(key);
}
public void set(String key, String value) {
dataMap.put(key, value);
}
public void delete(String key) {
dataMap.remove(key);
}
}
public class IIR {
private Map<String, String> dataMap = new ConcurrentHashMap<>();
public String get(String key) {
return dataMap.get(key);
}
public void set(String key, String value) {
dataMap.put(key, value);
}
public void del(String key) {
dataMap.remove(key);
}
}
public class RedisUtils {
private Map<String, String> dataMap = new ConcurrentHashMap<>();
public String get(String key) {
return dataMap.get(key);
}
public void set(String key, String value) {
dataMap.put(key, value);
}
public void del(String key) {
dataMap.remove(key);
}
}
Redis使用
public interface CacheService {
String get(final String key, int type);
void set(String key, String value, int type);
void del(String key, int type);
}
public class CacheServiceImpl implements CacheService {
private final RedisUtils redisUtils = new RedisUtils();
private final EGM egm = new EGM();
private final IIR iir = new IIR();
@Override
public String get(String key, int type) {
if (type == 1) {
return egm.gain(key);
} else if (type == 2) {
return iir.get(key);
}
return redisUtils.get(key);
}
@Override
public void set(String key, String value, int type) {
if (type == 1) {
egm.set(key, value);
} else if (type == 2) {
iir.set(key, value);
}
redisUtils.set(key, value);
}
@Override
public void del(String key, int type) {
if (type == 1) {
egm.delete(key);
} else if (type == 2) {
iir.del(key);
}
redisUtils.del(key);
}
}
改进方案 Link to heading
工程结构
├─application
│ ├─CacheService.java
│ └─CacheServiceImpl.java
├─factory
│ ├─JDKInvocationHandler.java
│ └─JDKProxyFactory.java
├─util
│ └─CalssLoaderUtils.java
└─workshop
├─impl
│ ├─EGMCacheAdapter.java
│ └─IIRCacheAdapter.java
└─ICacheAdapter.java
工程主要分为三个部分:工厂包(facoty),工具包(util),车间包(workshop)。
- 工厂包:JDKProxyFactory、JDKInvocationHandler两个类是代理类的定义和实现,这部分代码主要通过代理类和反射调用的方式获取工厂及方法调用。
- 工具包:ClassLoaderUtils类主要用于支撑反射的方法调用中的参数的处理。
- 车间包:EGMCacheAdapter、IIRCacheAdapter两个类主要是通过适配器的方式使用两个不同的集群方案。
实现集训适配器接口
public interface ICacheAdapter {
String get(String key);
void set(String key, String value);
void delete(String key);
}
public class EGMCacheAdapter implements ICacheAdapter {
private EGM egm = new EGM();
@Override
public String get(String key) {
return egm.gain(key);
}
@Override
public void set(String key, String value) {
egm.set(key, value);
}
@Override
public void delete(String key) {
egm.delete(key);
}
}
public class IIRCacheAdapter implements ICacheAdapter {
private IIR iir = new IIR();
@Override
public String get(String key) {
return iir.get(key);
}
@Override
public void set(String key, String value) {
iir.set(key, value);
}
@Override
public void delete(String key) {
iir.del(key);
}
}
代理方式的抽象工厂
public class JDKProxyFactory {
public static <T> T getProxy(Class cacheClazz, Class<? extends ICacheAdapter> cacheAdapter) throws Exception {
InvocationHandler handler = new JDKInvocationHandler(cacheAdapter.newInstance());
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
return (T) Proxy.newProxyInstance(classLoader, new Class[]{cacheClazz}, handler);
}
}
这里有一点非常重要,为什么要选择代理方式去实现抽象工厂。
因为把原来的单体Redis服务替换为两套Redis集群服务EGM和IIR,在不破坏原有单体Redis服务和实现类的额情况下,也就是CacheServiceImpl,通过一个代理类的方式实现另一个处理类,就可以非常方便的在Spring、SpringBoot等框架中通过注入的方式替换原有的Impl实现。
反射调用方法
public class JDKInvocationHandler implements InvocationHandler {
private ICacheAdapter cacheAdapter;
public JDKInvocationHandler(ICacheAdapter cacheAdapter) {
this.cacheAdapter = cacheAdapter;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
return ICacheAdapter.class.getMethod(method.getName(), ClassLoaderUtils.getClassByArgs(objects))
.invoke(cacheAdapter, objects);
}
}
不同redis的方法调用会在这里体现