工厂模式

理论

工厂模式又称工厂方法模式,是一种创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例化对象的类型

它的主要意图是定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类执行

简单来说就是为了提高代码结构的拓展性,屏蔽功能类的具体实现逻辑,外部只需要调用。这也是去掉众多ifelse的方式。缺点也很明显,需要实现的类非常多,如何去维护,怎样降低开发成本。

案例

代码案例

总结

工厂模式是创建者模式,创建者模式是为了创建出屏蔽内部实现细节的类,这些类可以简化代码的维护,还能不断复用,进而优化代码。

工厂方法模式旨在让子类自己决定需要实例化哪一个工厂类。

image-20250104111043977

image-20250104111207529

image-20250104113216062

抽象工厂模式

理论

抽象工厂模式和工厂方法模式都是为了解决接口选择问题。但在实现上,抽象工厂是一个中心工厂,创建其他工厂的模式。

工厂方法模式解决了一个系列的类的创建。而抽象工厂模式则解决了不同版本的一个系列的类的创建。

  • 这个设计模式满足了;单一职责原则、开闭原则、解耦等。但是随着业务的不断拓展,可能会造成类实现上的复杂度。可以通过引入其他设计模式和代理类以及自动生成加载的方式降低此项缺点。

案例

项目有多个Redis服务,每个Redis服务创建的时期不一样,各类操作的方法名不一样,并且以后可能还会拓展更多的Redis服务,使用抽象工厂模式来简化代码。

准备三个集群的代码

// 单体Redis服务
public class RedisUtils {

private Logger logger = LoggerFactory.getLogger(RedisUtils.class);
private Map<String, String> dataMap = new ConcurrentHashMap<>();

public String get(String key) {
logger.info("Redis获取数据 key:{}", key);
return dataMap.get(key);
}

public void set(String key, String value) {
logger.info("Redis写入数据 key:{} val:{}", key, value);
dataMap.put(key, value);
}

public void set(String key, String value, long timeout, TimeUnit timeUnit) {
logger.info("Redis写入数据 key:{} val:{} timeout:{} timeUnit:{}", key, value, timeout, timeUnit.toString());
dataMap.put(key, value);
}

public void del(String key) {
logger.info("Redis删除数据 key:{}", key);
dataMap.remove(key);
}
}
// 集群EGM
public class EGM {

private Logger logger = LoggerFactory.getLogger(EGM.class);

private Map<String, String> dataMap = new ConcurrentHashMap<String, String>();

public String gain(String key) {
logger.info("EGM获取数据 key:{}", key);
return dataMap.get(key);
}

public void set(String key, String value) {
logger.info("EGM写入数据 key:{} val:{}", key, value);
dataMap.put(key, value);
}

public void setEx(String key, String value, long timeout, TimeUnit timeUnit) {
logger.info("EGM写入数据 key:{} val:{} timeout:{} timeUnit:{}", key, value, timeout, timeUnit.toString());
dataMap.put(key, value);
}

public void delete(String key) {
logger.info("EGM删除数据 key:{}", key);
dataMap.remove(key);
}
}

// 集群 IIR
public class IIR {

private Logger logger = LoggerFactory.getLogger(IIR.class);

private Map<String, String> dataMap = new ConcurrentHashMap<String, String>();

public String get(String key) {
logger.info("IIR获取数据 key:{}", key);
return dataMap.get(key);
}

public void set(String key, String value) {
logger.info("IIR写入数据 key:{} val:{}", key, value);
dataMap.put(key, value);
}

public void setExpire(String key, String value, long timeout, TimeUnit timeUnit) {
logger.info("IIR写入数据 key:{} val:{} timeout:{} timeUnit:{}", key, value, timeout, timeUnit.toString());
dataMap.put(key, value);
}

public void del(String key) {
logger.info("IIR删除数据 key:{}", key);
dataMap.remove(key);
}
}

环境指出,我们的系统正在大量的使用Redis服务,但是因为系统不能满足业务的快速发展,因此需要迁移到集群服务中。而这时有两套集群服务需要兼容使用,又要满足所有的业务系统改造的同时不影响线上使用。

单体Redis使用代码

// 接口
public interface CacheService {

String get(final String key);

void set(String key, String value);

void set(String key, String value, long timeout, TimeUnit timeUnit);

void del(String key);

}
// 实现
public class CacheServiceImpl implements CacheService {

private RedisUtils redisUtils = new RedisUtils();

public String get(String key) {
return redisUtils.get(key);
}

public void set(String key, String value) {
redisUtils.set(key, value);
}

public void set(String key, String value, long timeout, TimeUnit timeUnit) {
redisUtils.set(key, value, timeout, timeUnit);
}

public void del(String key) {
redisUtils.del(key);
}

}

使用IFELSE改造为满足所有Redis服务的使用方式

// 接口
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);
}

// 实现
public class CacheServiceImpl implements CacheService {

private EGM egm = new EGM();

private IIR iir = new IIR();

private RedisUtils redisUtils = new RedisUtils();

@Override
public String get(String key, int redisType) {

if (1 == redisType) {
return egm.gain(key);
}

if (2 == redisType) {
return iir.get(key);
}

return redisUtils.get(key);
}

@Override
public void set(String key, String value, int redisType) {

if (1 == redisType) {
egm.set(key, value);
return;
}

if (2 == redisType) {
iir.set(key, value);
return;
}

redisUtils.set(key, value);
}

@Override
public void set(String key, String value, long timeout, TimeUnit timeUnit, int redisType) {

if (1 == redisType) {
egm.setEx(key, value, timeout, timeUnit);
return;
}

if (2 == redisType) {
iir.setExpire(key, value, timeout, timeUnit);
return;
}

redisUtils.set(key, value, timeout, timeUnit);
}

@Override
public void del(String key, int redisType) {

if (1 == redisType) {
egm.delete(key);
return;
}

if (2 == redisType) {
iir.del(key);
return;
}

redisUtils.del(key);
}
}
// 单元测试
@Test
public void test_CacheService() {

CacheService cacheService = new CacheServiceImpl();

cacheService.set("user_name_01", "Kin.", 2);
String val01 = cacheService.get("user_name_01", 2);
System.out.println("测试结果:" + val01);

}
// 结果
2025-01-05 10:54:20.592 INFO main (IIR.java:22) | IIR写入数据 key:user_name_01 val:Kin.
2025-01-05 10:54:20.593 INFO main (IIR.java:17) | IIR获取数据 key:user_name_01
测试结果:Kin.

使用抽象工厂模式重构代码

本次抽象工厂的创建和获取方式,会采用代理类的方式进行实现。所被代理的类就是目前的Redis操作方法类,让这个类在不需要任何修改的情况下就可以实现调用集群A和集群B的数据服务。

由于集群A和集群B在部分方法名上是不同的,因此需要做一个接口适配,而这个适配类就相当于工厂中的工厂,用于创建把不同的服务抽象为统一的接口做相同的业务。

  • 为了解决不同集群功能名的区别,需要适配器,有多少个集群就需要写多少个适配器,每个适配器也就是那个集群的标识,不同集群的适配器是同类型的产品,所以可以用工厂方法模式来创建。构建一个统一的适配器接口

    // 适配器接口
    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);

    }
    // 实现具体的功能

    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 set(String key, String value, long timeout, TimeUnit timeUnit) {
    egm.setEx(key, value, timeout, timeUnit);
    }

    @Override
    public void del(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 set(String key, String value, long timeout, TimeUnit timeUnit) {
    iir.setExpire(key, value, timeout, timeUnit);
    }

    @Override
    public void del(String key) {
    iir.del(key);
    }
    }

  • 抽象工厂需要解决的问题是针对不同的集群,实现统一的调用,所以需要定义统一调用的接口,抽象工厂产生的是各个工厂,也就是适配器,这里使用代理模式,实现根据适配器类型来自动生成对应类型的工厂

    public interface CacheService {

    String get(final String key);

    void set(String key, String value);

    void set(String key, String value, long timeout, TimeUnit timeUnit);

    void del(String key);
    }
    // 代理类执行逻辑
    public class JDKInvocationHandler implements InvocationHandler {

    private ICacheAdapter cacheAdapter;

    public JDKInvocationHandler(ICacheAdapter cacheAdapter) {
    this.cacheAdapter = cacheAdapter;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    return ICacheAdapter.class.getMethod(method.getName(), ClassLoaderUtils.getClazzByArgs(args)).invoke(cacheAdapter, args);
    }
    }
    // 代理类创建逻辑
    /**
    *
    * @param interfaceClass 需要的产品类型
    * @param cacheAdapter 委托生产的工厂
    */
    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);
    }
  • 测试

    public class ApiTest {

    @Test
    public void test_CacheService() throws Exception {

    CacheService proxy_EGM = JDKProxy.getProxy(CacheServiceImpl.class, new EGMCacheAdapter());
    proxy_EGM.set("user_name_01", "kin.");
    String val01 = proxy_EGM.get("user_name_01");
    System.out.println("测试结果:" + val01);

    CacheService proxy_IIR = JDKProxy.getProxy(CacheServiceImpl.class, new IIRCacheAdapter());
    proxy_IIR.set("user_name_01", "kin.");
    String val02 = proxy_IIR.get("user_name_01");
    System.out.println("测试结果:" + val02);

    }
    }
    025-01-05 11:32:59.404 INFO main (EGM.java:22) | EGM写入数据 key:user_name_01 val:kin.
    2025-01-05 11:32:59.407 INFO main (EGM.java:17) | EGM获取数据 key:user_name_01
    测试结果:kin.
    2025-01-05 11:32:59.407 INFO main (IIR.java:22) | IIR写入数据 key:user_name_01 val:kin.
    2025-01-05 11:32:59.407 INFO main (IIR.java:17) | IIR获取数据 key:user_name_01
    测试结果:kin.

总结

image-20250105113504836