设计模式学习笔记-3 创建者模式-抽象工厂模式
抽象工厂模式与工厂模式虽然都是为了解决接口选择问题,但是在实现上,抽象工厂时一个中心工厂,创建其他工厂的模式。
场景模拟
redis由单机升级为集群后代码的处理
模拟单机服务RedisUtils
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@Slf4j
public class RedisUtils {
private final Map<String, String> dataMap = new ConcurrentHashMap<>();
public String get(String key) {
log.info("redis获取数据 key:{}", key);
return dataMap.get(key);
}
public void set(String key, String value) {
log.info("redis写入数据 key:{} value:{}", key, value);
dataMap.put(key, value);
}
public void del(String key) {
log.info("redis删除数据 key:{}", key);
dataMap.remove(key);
}
public void set(String key, String value, long timeout, TimeUnit timeUnit) {
log.info("redis写入数据 key:{} value:{} timeout:{} timeUnit:{}", key, value, timeout, timeUnit.toString());
dataMap.put(key, value);
}
}
- 模拟Redis功能,假定目前所有系统都在使用的服务
- 类和方法名都需要写死在所有业务中,改动麻烦
模拟集群EGM
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@Slf4j
public class EGM {
private final Map<String, String> dataMap = new ConcurrentHashMap<>();
public String gain(String key) {
log.info("EGM获取数据 key:{}", key);
return dataMap.get(key);
}
public void set(String key, String value) {
log.info("EGM写入数据 key:{} value:{}", key, value);
dataMap.put(key, value);
}
public void setEx(String key, String value, long timeout, TimeUnit timeUnit) {
log.info("EGM写入数据 key:{} value:{} timeout:{} timeunit:{}", key, value, timeout, timeUnit);
dataMap.put(key, value);
}
public void delete(String key) {
log.info("EGM删除数据 key:{}", key);
dataMap.remove(key);
}
}
- 模拟一个集群服务,但是方法名与各业务系统中使用的方法名不同(做一样的事儿,但是操作不同)
模拟集群IIR
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@Slf4j
public class IIR {
private final Map<String, String> dataMap = new ConcurrentHashMap<>();
public String get(String key) {
log.info("IIR获取数据 key:{}", key);
return dataMap.get(key);
}
public void set(String key, String value) {
log.info("IIR写入数据 key:{} value:{}", key, value);
dataMap.put(key, value);
}
public void setExpire(String key, String value, long timeout, TimeUnit timeUnit) {
log.info("IIR写入数据 key:{} value:{} timeout:{} timeunit:{}", key, value, timeout, timeUnit);
dataMap.put(key, value);
}
public void del(String key) {
log.info("IIR删除数据 key:{}", key);
dataMap.remove(key);
}
}
- 另外一套集群服务,即为了做同样一件事儿,第三种操作
使用if else
实现
定义接口
import java.util.concurrent.TimeUnit;
public interface CacheService {
String get(final String key, int redisType);
void set(String key, String value, int redisType);
void set(String key, String value, long timeout, TimeUnit timeUnit, int redisType);
void del(String key, int redisType);
}
实现接口
import com.yaroo.head_first_demo.design_pattern.demo2_01.CacheService;
import com.yaroo.head_first_demo.design_pattern.demo2_01.RedisUtils;
import com.yaroo.head_first_demo.design_pattern.demo2_01.matter.EGM;
import com.yaroo.head_first_demo.design_pattern.demo2_01.matter.IIR;
import java.util.concurrent.TimeUnit;
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 redisType) {
if (redisType == 1) {
return egm.gain(key);
}
if (redisType == 2) {
return iir.get(key);
}
return redisUtils.get(key);
}
@Override
public void set(String key, String value, int redisType) {
if (redisType == 1) {
egm.set(key, value);
return;
}
if (redisType == 2) {
iir.set(key, value);
return;
}
redisUtils.set(key, value);
}
@Override
public void set(String key, String value, long timeout, TimeUnit timeUnit, int redisType) {
if (redisType == 1) {
egm.setEx(key, value, timeout, timeUnit);
return;
}
if (redisType == 2) {
iir.setExpire(key, value, timeout, timeUnit);
return;
}
redisUtils.set(key, value, timeout, timeUnit);
}
@Override
public void del(String key, int redisType) {
if (redisType == 1) {
egm.delete(key);
return;
}
if (redisType == 2) {
iir.del(key);
return;
}
redisUtils.del(key);
}
}
单元测试
import com.yaroo.head_first_demo.design_pattern.demo2_01.impl.CacheServiceImpl;
import org.junit.jupiter.api.Test;
class CacheServiceTest {
@Test
public void test_CacheService() {
CacheService cacheService = new CacheServiceImpl();
cacheService.set("name01", "yaroo", 1);
String name01 = cacheService.get("name01", 1);
System.out.println(name01);
}
}
测试结果:
17:26:21.959 [main] INFO com.yaroo.head_first_demo.design_pattern.demo2_01.matter.EGM - EGM写入数据 key:name01 value:yaroo
17:26:21.962 [main] INFO com.yaroo.head_first_demo.design_pattern.demo2_01.matter.EGM - EGM获取数据 key:name01
yaroo
使用抽象工厂模式重构
定义适配接口
import java.util.concurrent.TimeUnit;
public interface ICacheAdapter {
String get(String key);
void set(String key, String value);
void set(String key, String value, long timeout, TimeUnit timeUnit);
void del(String key);
}
- 该类主要作用是让所有集群服务的提供方都在统一的名称下进行操作。便于后期业务拓展
定义集群服务
EGMCacheAdapter
import com.yaroo.head_first_demo.design_pattern.demo2_01.factory.ICacheAdapter;
import com.yaroo.head_first_demo.design_pattern.demo2_01.matter.EGM;
import java.util.concurrent.TimeUnit;
public class EGMCacheAdapter implements ICacheAdapter {
private final 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 set(String key, String value, long timeout, TimeUnit timeUnit) {
egm.setEx(key, value, timeout, timeUnit);
}
@Override
public void del(String key) {
egm.delete(key);
}
}
IIRCacheAdapter
import com.yaroo.head_first_demo.design_pattern.demo2_01.factory.ICacheAdapter;
import com.yaroo.head_first_demo.design_pattern.demo2_01.matter.IIR;
import java.util.concurrent.TimeUnit;
public class IIRCacheAdapter implements ICacheAdapter {
private final 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 set(String key, String value, long timeout, TimeUnit timeUnit) {
iir.setExpire(key, value, timeout, timeUnit);
}
@Override
public void del(String key) {
iir.del(key);
}
}
定义抽象工程代理类和实现类
JDKProxy
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class JDKProxy {
public static <T> T getProxy(Class<T> interfaceClass, ICacheAdapter cacheAdapter) throws Exception {
InvocationHandler handler = new JDKInvocationHandler(cacheAdapter);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Class<?>[] classes = interfaceClass.getInterfaces();
return (T) Proxy.newProxyInstance(classLoader, new Class[]{classes[0]}, handler);
}
}
- 该代理类的主要作用是使用哪个集群有外部通过入参进行传递
JDKInvocationHandler
public class JDKInvocationHandler implements InvocationHandler {
private ICacheAdapter cacheAdapter;
public JDKInvocationHandler(ICacheAdapter cacheAdapter) {
this.cacheAdapter = cacheAdapter;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return ICacheAdapter.class.getMethod(method.getName(), ClassLoaderUtils.getClazzByArgs(args)).invoke(cacheAdapter, args);
}
}
- 在
invoke
中通过使用获取方法名称反射方式,调用对应的方法功能。
总结
- 抽象工厂模式主要为解决一个产品族问题,存在多个不同类型的产品(如redis集群,操作系统)时进行接口选择的问题。
- 抽象工厂模式满足了单一职责,开闭原则,解耦等优点。