创建型-抽象工厂模式 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的方法调用会在这里体现