什么是系统融合
何谓系统融合,简单的说就是把两个或者多个系统融合为一个系统。在一个公司中,为了降低维护成本,会遇到将两个或者多个业务类似的系统融合成一个系统的场景。有人会问为什么同一个公司会有多个类似的系统,最常见的就是A电商公司收购了B电商公司,为了降低维护成本就需要对两套系统进行融合。当然还有一些原因是公司内耗,这里不一一列举。
两个系统融合,最大的困难就是接口不统一。比如同样是商品接口,A、B两个公司的接口名可能不同,商品类的定义也不同。这时为了让外部系统调用这两个接口无感知,就需要一个统一的接口,这就产生了适配器模式。
适配器模式
在解决系统融合之前,先来看看“适配器模式”。生活中经常会遇到适配器,比如:把电器的两项插头转换为三项插头;笔记本电脑的电流适配器,把220伏转换为12伏;自来水管分流口等等。他们都有一个共同的特征,在原本不匹配的两个接口之间建立起桥梁,把二者链接起来。换成程序员的语言即为:将一个类的接口,转换成客户期望的另一个接口,适配器让原本接口不兼容的类连接起来。适配器模式类图:
类图很简单,大致分为四类角色:
1、客户端Client,该客户端只接受Target类型的接口。
2、抽象接口Target(抽象类也可以),定义了公共的方法。
3、 适配器Adapter,实现了Target接口,并拥有一个真实的Adaptee类型的成员变量adaptee。在其request方法中会调用adaptee的realRequest方法。
4、被适配角色Adaptee,realRequest方法包含真实的业务逻辑实现。
使用适配器模式进行系统融合
上面类图中只画出了一个Adapter适配器,在“系统融合”的场景中会为同一个接口创建多个Adapter适配器(这里是两个),分别对应多个类似业务。这里以A、B两个电商系统融合为例,两套系统有数十个接口我们需要在A、B两个系统之上新建一个“适配器”系统。为了顺应现在的“前中台系统建设”潮流,设计架构上对前中台进行区分,整体架构调整如下:
在A、B两个系统没有融合前,他们都各自对应自己的前台系统,架构说明如下:
1、A、B两个公司合并前,都有各自对应的前台系统和中台系统。如图中“绿色箭头”所示。
2、现在A、B两个公司合并,为了降低维护成本,以及增加用户体验,只维护一个前台系统。为了在系统融合期间,外部用户可以正常访问A、B前台系统,这里增加一个“新前台系统”。
3、同时为了兼容老数据,A、B两个系统保持原样不变,新增一个“适配器系统”,对A、B两个系统中的公共业务接口进行适配。接口调用流程,如上图中“红色箭头所示”,统一后的“前台系统”首先调用“适配器系统”,根据参数适配到A或B系统中。
4、A、B两套系统在融合前 虽然业务类似,但也就自己的个性化业务,统一后的“前台系统”直接调用A、B系统原接口即可。如上图中的“紫色箭头”所示。
5、当“新前台系统”开发完成并上线后,即可关闭两个老的前台系统。只维护一套“新前台系统”即可
通过上述系统架构,即可快速完成新系统的融合,又不影响老系统的访问,为了防止老客户对新系统的不适应,还可以让“三个前台系统”并行运行一段时间。是不是有种“酷毙了”的赶脚。
这个强大的系统架构设计的核心就是设计新的“适配器系统”,这个系统里设计有多个数据接口(A、B系统公共的接口),每个接口都是采用“适配器模式”对A、B两个系统的接口进行封装,让“新的前台系统”以为是一个接口。
下面就以“商品接口”为例,对“适配器模式”进行讲解。
适配器示例--融合“商品接口”
根据上述新系统架构,主要分为4个系统:“A系统”、“B系统”、新“适配器系统”、新“前台系统”。作为示例不会把4个系统都搬出来,这里使用一个java application程序进行模拟,如下:
其中两个老系统的商品类ProductA、ProductB业务很类似:
public class ProductA { private Integer a_id;//A系统 商品id private String a_name;//A系统 商品名称 private String a_category;//A系统 商品分类 //省略其他setter、getter方法 } public class ProductB { private Integer b_id;//B系统 商品id private String b_name;//B系统 商品名称 private String b_category;//B系统 商品分类 private Integer venderId;//商家Id //省略其他setter、getter方法 }
可以看到其中三个字段是相同业务类型,ProductB中多一个成员变量venderId(商家Id)。现在要在新“适配器系统”中,定义新商品类Product,需要包含两个系统中所有业务,定义如下:
public class Product { private Integer id;//新商品id private String name;//新商品名称 private String category;//新商品分类 private Integer venderId;//商家Id //省略其他setter getter方法 }
新商品对象定义完毕,现在进行接口“适配”,这里以A系统商品接口为例(B系统类似):
被适配角色ProdcutManagerA(接口)、ProdcutManagerAImpl(实现类):
public interface ProdcutManagerA { ProductA getById(Integer id); } public class ProdcutManagerAImpl implements ProdcutManagerA{ public ProductA getById(Integer id){ ProductA productA = new ProductA(); productA.setA_id(1000); productA.setA_name("A系统彩电"); productA.setA_category("A系统_家电类"); return productA; } }
新接口:新接口返回类型是新商品类Product:
/** * 新商品管理接口类 * Created by gantianxing on 2017/11/4. */ public interface ProdcutAdapter { //新商品接口 Product getById(Integer id); }
A系统适配器 需要实现新接口:
/** * A系统商品接口 适配器 * Created by gantianxing on 2017/11/4. */ public class ProdcutAdapterAImpl implements ProdcutAdapter { private ProdcutManagerA prodcutManagerA; public ProdcutAdapterAImpl(ProdcutManagerA prodcutManagerA){ this.prodcutManagerA = prodcutManagerA; } public Product getById(Integer id) { ProductA productA = prodcutManagerA.getById(id); if(productA != null){ Product product = new Product(); product.setId(productA.getA_id()); product.setName(productA.getA_name()); product.setCategory(productA.getA_category()); return product; } return null; } }
可以看到ProdcutAdapterAImpl适配器,把“A系统”商品接口 转换为“新前台系统适配的”接口。这里只展示类A系统适配器创建过程,B系统适配器ProdcutAdapterBImpl创建过程类似,不再累述。
下面是见证奇迹的时候,“新前台系统”模拟类Client:可以不依赖A、B系统的情况下,采用统一的接口调用老A、B系统的商品接口:
/** * 客户端,模拟前台系统 * Created by gantianxing on 2017/11/4. */ public class Client { public static void main(String[] args) { //新建 两个适配器对象,在真实系统中 通过使用rpc框架 客户端只会直接依赖ProdcutAdapter接口 ProdcutAdapter prodcutAdapterAImpl = new ProdcutAdapterAImpl(new ProdcutManagerAImpl()); ProdcutAdapter prodcutAdapterBImpl = new ProdcutAdapterBImpl(new ProdcutManagerBImpl()); Product p1 = prodcutAdapterAImpl.getById(1000);//获取A系统商品 1000 Product p2 = prodcutAdapterBImpl.getById(1001);//获取B系统商品 1001 System.out.println(p1.toString()); System.out.println(p2.toString()); } }
在本实例中,也许已经注意到Client中依赖了新老系统中的具体的实现类:ProdcutAdapterAImpl、ProdcutManagerAImpl、ProdcutAdapterBImpl、ProdcutManagerBImpl。
结合RPC框架进行解耦
但在真实的系统中通过引入RPC框架和Spring IOC注入,“新前台系统”只会依赖一个“适配器”接口类:ProdcutAdapter;同时新建的“适配器系统”只依赖老A、B系统的接口类:ProdcutManagerA、ProdcutManagerB。如下图所示:
可以看到“新前台系统”、以及新增的“适配器系统”只依赖接口,不依赖具体的实现,与A、B系统做到完全解耦。
也许你已注意到 上图有点类似适配器模式的类图,其实这个适配器系统 就是利用“适配器模式”为基础 封装出的一个新系统而已(把模式放大化使用)。
该模式的要点,就是转换接口以适配客户端业务需求。 关于适配器模式就总结到这里。
出处:
http://moon-walker.iteye.com/blog/2398721