首页 > 其他分享 >设计模式-创建者模式-抽象工厂模式

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

时间:2022-10-18 17:48:12浏览次数:90  
标签:set String 创建者 void 模式 集群 key 设计模式 public

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

内容摘自:重学 Java 设计模式:实战抽象工厂模式「替换Redis双集群升级,代理类抽象场景」 | bugstack 虫洞栈

抽象工厂模式介绍

抽象工厂模式,图片来自 refactoringguru.cn

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

可能在平常的业务开发中很少关注这样的设计模式或者类似的代码结构,但是这种场景确一直在我们身边,例如;

  1. 不同系统内的回车换行
    1. Unix系统里,每行结尾只有 <换行>,即 \n
    2. Windows系统里面,每行结尾是 <换行><回车>,即 \n\r
    3. Mac系统里,每行结尾是 <回车>

案例场景模拟

模拟企业级双套Redis集群升级

预估QPS较低系统压力较小并发访问不大近一年没有大动作等等,在考虑时间投入成本的前提前,并不会投入特别多的人力去构建非常完善的系统。就像对 Redis 的使用,往往可能只要是单机的就可以满足现状。

但随着业务超过预期的快速发展,系统的负载能力也要随着跟上。原有的单机 Redis 已经满足不了系统需求。这时候就需要更换为更为健壮的Redis集群服务,虽然需要修改但是不能影响目前系统的运行,还要平滑过渡过去。

随着这次的升级,可以预见的问题会有;

  1. 很多服务用到了Redis需要一起升级到集群。
  2. 需要兼容集群A和集群B,便于后续的灾备。
  3. 两套集群提供的接口和方法各有差异,需要做适配。
  4. 不能影响到目前正常运行的系统。

1. 场景模拟工程

itstack-demo-design-2-00
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                ├── matter
                │   ├── EGM.java
                │   └── IIR.java
                └── RedisUtils.java

2. 场景简述

2.1 模拟单机服务 RedisUtils

Redis单机服务

  • 模拟Redis功能,也就是假定目前所有的系统都在使用的服务
  • 类和方法名次都固定写死到各个业务系统中,改动略微麻烦

2.2 模拟集群 EGM

模拟集群 EGM

  • 模拟一个集群服务,但是方法名与各业务系统中使用的方法名不同。有点像你mac,我用win。做一样的事,但有不同的操作。

2.3 模拟集群 IIR

模拟集群 IIR

  • 这是另外一套集群服务,有时候在企业开发中就很有可能出现两套服务,这里我们也是为了做模拟案例,所以添加两套实现同样功能的不同服务,来学习抽象工厂模式。

综上可以看到,我们目前的系统中已经在大量的使用redis服务,但是因为系统不能满足业务的快速发展,因此需要迁移到集群服务中。而这时有两套集群服务需要兼容使用,又要满足所有的业务系统改造的同时不影响线上使用。

3. 单集群代码使用

以下是案例模拟中原有的单集群Redis使用方式,后续会通过对这里的代码进行改造。

当前功能的类图结构

3.1 定义使用接口

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

}  

3.2 实现调用代码

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

}   
  • 目前的代码对于当前场景下的使用没有什么问题,也比较简单。但是所有的业务系统都在使用同时,需要改造就不那么容易了。这里可以思考下,看如何改造才是合理的。

抽象工厂模式重构代码

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

并且这里还有一点非常重要,由于集群A和集群B在部分方法提供上是不同的,因此需要做一个接口适配,而这个适配类就相当于工厂中的工厂,用于创建把不同的服务抽象为统一的接口做相同的业务。这一块与我们上一章节中的工厂方法模型类型,可以翻阅参考。

1. 工程结构

itstack-demo-design-2-02
└── src
    ├── main
    │   └── java
    │       └── org.itstack.demo.design
    │           ├── factory    
    │           │   ├── impl
    │           │   │   ├── EGMCacheAdapter.java 
    │           │   │   └── IIRCacheAdapter.java
    │           │   ├── ICacheAdapter.java
    │           │   ├── JDKInvocationHandler.java
    │           │   └── JDKProxy.java
    │           ├── impl
    │           │   └── CacheServiceImpl.java    
    │           └── CacheService.java 
    └── test
         └── java
             └── org.itstack.demo.design.test
                 └── ApiTest.java    

抽象工厂模型结构

抽象工厂模型结构

  • 工程中涉及的部分核心功能代码,如下;
    • ICacheAdapter,定义了适配接口,分别包装两个集群中差异化的接口名称。EGMCacheAdapterIIRCacheAdapter
    • JDKProxyJDKInvocationHandler,是代理类的定义和实现,这部分也就是抽象工厂的另外一种实现方式。通过这样的方式可以很好的把原有操作Redis的方法进行代理操作,通过控制不同的入参对象,控制缓存的使用。

,那么接下来会分别讲解几个类的具体实现。

2. 代码实现

2.1 定义适配接口

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

}    
  • 这个类的主要作用是让所有集群的提供方,能在统一的方法名称下进行操作。也方面后续的拓展。

2.2 实现集群使用服务

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

}   
  • 以上两个实现都非常容易,在统一方法名下进行包装。

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

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中通过使用获取方法名称反射方式,调用对应的方法功能,也就简化了整体的使用。
  • 到这我们就已经将整体的功能实现完成了,关于抽象工厂这部分也可以使用非代理的方式进行实现。

3. 测试验证

编写测试类:

@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);
}   
  • 在测试的代码中通过传入不同的集群类型,就可以调用不同的集群下的方法。JDKProxy.getProxy(CacheServiceImpl.class, new EGMCacheAdapter());
  • 如果后续有扩展的需求,也可以按照这样的类型方式进行补充,同时对于改造上来说并没有改动原来的方法,降低了修改成本。

标签:set,String,创建者,void,模式,集群,key,设计模式,public
From: https://www.cnblogs.com/zhangj9/p/16803415.html

相关文章

  • Rt3070芯片动态获取IP地址——station模式
    ———————————————————————————————————————主机操作系统:Centos6.7交叉编译器环境:arm-linux-gcc-4.5.4 开发板平台:FL2440 Linux......
  • 完全分布式运行模式
    完全分布式运行模式配置步骤1.准备三台客户机关闭防火墙静态IP主机名2.安装JDK3.配置环境变量4.安装Hadoop5.配置环境变量6.配置集群7.单点启动8.配置SSH9.集......
  • 用 Kotlin 的函数式编程 替代 GOF 设计模式
    用Kotlin的函数式编程替代GOF设计模式函数式编程(FP)值就是函数,函数就是值。所有函数都消费函数,所有函数都生产函数。"函数式编程",又称泛函编程,是一种"编程范式"(prog......
  • React组件设计模式-纯组件,函数组件,高阶组件
    一、组件(1)函数组件如果你想写的组件只包含一个render方法,并且不包含state,那么使用函数组件就会更简单。我们不需要定义一个继承于React.Component的类,我们可以定......
  • 华为AirEngine 5760-22W Web示例(V200R021C00SPC200版本):NAT网关模式(DHCP方式入网)【
    说明:此版本存在一些默认的WLAN无线业务配置,AP上电启动后,默认创建名称为“HUAWEI-XXXX”的SSID,其中XXXX是APMAC地址的后四位。默认无线业务覆盖使用无认证方式,用户搜索到该......
  • 数字化转型后企业的管理模式有哪些改变呢?
    数字化转型后的企业在管理模式上整体呈现的就是数字化的管理,这是因为数字化转型的主题就是要构建“业务数字化、数字资产化、资产服务化、服务业务化”闭环,通过数字化技......
  • Java基于解释器模式实现定义一种简单的语言功能示例
    本文实例讲述了Java基于解释器模式实现定义一种简单的语言功能。分享给大家供大家参考,具体如下:一模式定义解释器模式:就是给定一个语言的文法表示,并且定义一个解释器,用来......
  • docker 安装mysql主从模式
    1.创建主数据库容器dockerrun-p3306:3306--namemysql-master-v/zgbo/mysql_m/log:/var/log/mysql-v/zgbo/mysql_m/data:/var/lib/mysql-v/zgbo/mysql_m/conf:......
  • JavaWeb之MVC开发模式之商品实例CURD详解
    三层MVC开发模式架构使用自己设计的商城实体类创建对应的数据库表和类完成增删改查管理模块。(1)不使用MVC设计模式完成增删改查管理功能。(2)使用MVC设计模式完成增删改查管......
  • mybaties 批量插入的最佳模式 :ExecutorType.BATCH 模式
    1.0两种插入方式 mybaties中向数据库中插入批量插入数据,有两种方法。一种是使用mybaties的批量执行器模式。一种使用sql语句拼接的模式插入。2.0使用方式2.1o......