设计模式-创建者模式-抽象工厂模式

抽象工厂模式

案例:Redis集群

单集群的代码结构如下:

  • redis单击服务RedisUtils

    public class RedisUtils {
    
        private Logger logger = LoggerFactory.getLogger(RedisUtils.class);
    
        private Map<String, String> dataMap = new ConcurrentHashMap<String, String>();
    
        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);
    }
}

这里的方法名和上边的单击RedisUtils都不太一样

  • 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);
        }
    
    }
    

单集群代码使用

定义使用接口

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实现多集群

代码结构如下

  • CacheService

    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);
    
    }
    

这里增加了一个redisType参数来判断使用哪个集群

  • CacheServiceImpl

    public class CacheServiceImpl implements CacheService {
    
        private RedisUtils redisUtils = new RedisUtils();
    
        private EGM egm = new EGM();
    
        private IIR iir = new IIR();
    
        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);
        }
    
        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);
        }
    
        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);
        }
    
        public void del(String key, int redisType) {
    
            if (1 == redisType) {
                egm.delete(key);
                return;
            }
    
            if (2 == redisType) {
                iir.del(key);
                return;
            }
    
            redisUtils.del(key);
        }
    
    
    }
    

这⾥里里的实现过程⾮非常简单,主要根据类型判断是哪个Redis集群。

测试验证

public class ApiTest {

    @Test
    public void test_CacheService() {

        CacheService cacheService = new CacheServiceImpl();

        cacheService.set("user_name_01", "小傅哥", 1);
        String val01 = cacheService.get("user_name_01", 1);
        System.out.println("测试结果:" + val01);

    }

}

抽象工厂模式重构代码

重构后的代码结构如下:

工程中涉及的部分核⼼心功能代码,如下:

  • ICacheAdapter ,定义了适配接口,分别包装两个集群中差异化的接口名称。 EGMCacheAdapter 、 IIRCacheAdapter
  • JDKProxy 、 JDKInvocationHandler ,是代理理类的定义和实现,这部分也就是抽象工厂的另外一种实现⽅方式。通过这样的⽅方式可以很好的把原有操作Redis的⽅方法进⾏行行代理理操作,通过控制不不同的⼊入参对象,控制缓存的使用。

代码实现

  • 定义适配接口

    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

      public class EGMCacheAdapter implements ICacheAdapter {
      
          private EGM egm = new EGM();
      
          public String get(String key) {
              return egm.gain(key);
          }
      
          public void set(String key, String value) {
              egm.set(key, value);
          }
      
          public void set(String key, String value, long timeout, TimeUnit timeUnit) {
              egm.setEx(key, value, timeout, timeUnit);
          }
      
          public void del(String key) {
              egm.delete(key);
          }
      }
      
    • IIRCacheAdapter

      public class IIRCacheAdapter implements ICacheAdapter {
      
          private IIR iir = new IIR();
      
          public String get(String key) {
              return iir.get(key);
          }
      
          public void set(String key, String value) {
              iir.set(key, value);
          }
      
          public void set(String key, String value, long timeout, TimeUnit timeUnit) {
              iir.setExpire(key, value, timeout, timeUnit);
          }
      
          public void del(String key) {
              iir.del(key);
          }
      
      }
      

以上两个实现类,通过统一方法名来进行包装

  • 定义抽象⼯工程代理理类和实现

    • JDKProxy

      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;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return ICacheAdapter.class.getMethod(method.getName(), ClassLoaderUtils.getClazzByArgs(args)).invoke(cacheAdapter, args);
        }
    
    }
    

在代理类的实现中其实也⾮非常简单,通过穿透进来的集群服务进行⽅方法操作。

在 invoke 中通过使用获取方法名称反射方式,调⽤用对应的方法功能,也就简化了整体的使用。

  • 测试验证

    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", "小傅哥");
            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", "小傅哥");
            String val02 = proxy_IIR.get("user_name_01");
            System.out.println("测试结果:" + val02);
    
            CacheService redisService = new CacheServiceImpl();
            redisService.set("user_name_01", "小傅哥");
            String val03 = redisService.get("user_name_01");
            System.out.println("测试结果:" + val03);
        }
    
    }
    

这里的代理我的理解是,将对应的Adapter类来实现CacheService方法对应的方法

在测试中,调用proxy_EGM.set("user_name_01", "小傅哥"); 会先执行EGMCacheAdapter中的set方法,执行完会调用JDKInvocationHandler 里的invoke方法,通过使用获取方法名称反射方式,调⽤用对应的方法功能。

最终的测试结果:
20:12:51.415 [main] INFO  org.itstack.demo.design.matter.EGM - EGM写入数据 key:user_name_01 val:小傅哥
20:16:52.016 [main] INFO  org.itstack.demo.design.matter.EGM - EGM获取数据 key:user_name_01
测试结果:小傅哥
20:17:00.006 [main] INFO  org.itstack.demo.design.matter.IIR - IIR写入数据 key:user_name_01 val:小傅哥
20:17:00.658 [main] INFO  org.itstack.demo.design.matter.IIR - IIR获取数据 key:user_name_01
测试结果:小傅哥
20:17:01.055 [main] INFO  org.itstack.demo.design.RedisUtils - Redis写入数据 key:user_name_01 val:小傅哥
20:17:01.417 [main] INFO  org.itstack.demo.design.RedisUtils - Redis获取数据 key:user_name_01
测试结果:小傅哥

总结

这里就是将具体的适配器类和CachService实现类作为代理入参,通过invoke方法在调用适配器类的具体方法时,达到其实现CachService方法的目的

这个设计模式满足了:单一职责、开闭原则、解耦等优点,但如果说随着业务的不断拓拓展,可能会造成类实现上的复杂度。但也可以说算不上缺点,因为可以随着其他设计方式的引⼊入和代理类以及⾃自动生成加载的⽅方式降低此项缺点。

工厂方法模式和抽象工厂模式的区别于联系

  • 工厂方法模式:用来封装对象的创建。工厂方法模式(Factory Method Pattern)通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。这样,关于超类的代码和子类创建对象的代码之间就解耦了。

  • 抽象工厂模式(Abstract Factory Pattern):提供一个接口,用来创建相关或依赖对象的家族,而不需要明确指定具体类。这样,产品创建的过程只会依赖于接口,而不关心具体的实现是什么,从而达到解耦的目的。

  • 角色 - 工厂方法模式:
     1、抽象工厂(Creator):定义了一个抽象的工厂方法,让子类实现此方法制造产品。
     2、具体工厂(ConcreteCreator):实现抽象工厂方法,包含具体生产产品的实现代码,返回一个产品的实例。
     3、抽象产品(Product):定义一个抽象的产品,为了工厂中创建产品类型的匹配。
     4、具体产品(ConcreteProduct):实现抽象产品,包含各自产品的特色代码。

  • 角色 - 抽象工厂模式:
     1、抽象工厂(Creator):一般是接口或抽象类,定义了一系列的产品家族。
     2、具体工厂(ConcreteCreator):实现抽象工厂方法,包含一系列产品家族的实现代码。
     3、抽象产品(Product):定义一个抽象的产品,为了工厂中创建产品类型的匹配。
     4、具体产品(ConcreteProduct):实现抽象产品,包含各自产品的特色代码。

    抽象工厂模式在使用时,利用对象组合和多态将不同的工厂实现类注入到代码中。这样,代码只会依赖接口,根本不关心具体实现是什么,以此达到解耦的目的。

原文地址:https://www.cnblogs.com/RealGang/p/14614773.html