一、前言
从入职到现在已有一年。想想现在与当初自己的期望虽有遗憾但也还是有所进步。我个人对自己的认知是敢于尝试与实践新的技术与新的理论,说大胆也不为过。因此工作中写的代码或多或少也被诟病、被批评、被质疑。但我觉得若人人都循规蹈矩、人人都不去尝试,那么谈何创新、谈何进步呢?终究是需要人去做那一颗划破静夜的流星。
二、背景
最近在对项目的SDK进行升级改造。考虑到要兼容之前用户的调用习惯,所以改造是需要很多讲究的,也引发了对代码架构的思考。在大多数时候我们在完成一个需求的时候,大部分人都是努力去完成它,即便给时间优化,我们往往也从代码规范、性能上进行优化,似乎没有人考虑可扩展性。当然这需要一定的前瞻性、也或许是因为我们缺乏对该功能的全局意识不知道该功能未来会发生如何变化。
三、案例阐述
- 1,业务A-组合服务间的协作。
在该业务场景下,多线程处理且需要多个服务组合使用,有些服务需要依托其他服务的处理信息。一般我们的处理的思路是根据其他服务的返回值进行判断再做处理。如果我们不做任何思考,就按照这个思路做下去,我觉得这样子,代码结构相对混乱,代码可扩展性也很差。比如我现在给这个业务新加了一个服务,其他服务都需依赖我得服务处理信息进行处理,这就要对代码结构进行调整了,代价高昂。示例伪代码如下:
public class Business{ private ServiceA serviceA; private ServiceB serviceB; public void invoke(){ if(serviceA.doSomething()){ serviceB.doSomething(); } } } interface class ServiceA{ boolean doSomething(); } interface class ServiceB{ boolean doSomething(); }
现在我们思考是否有其他成熟的框架解决过类型的业务?我们知道Servlet对于多线程对当前应用的感知是通过Context完成的。受此启发,这种涉及到多线程、多服务的组合调用我们可以给该业务场景绑定一个context,我们可以再任何服务通过获取context信息,以确定当前服务是否可继续执行。调整的代码结构:
public class Business{ private ServiceA serviceA; private ServiceB serviceB; private Context context; public void invoke(){ serviceA.doSomething(); if(context.hasSomeServiceError()){ // end return ; } } } interface class ServiceA{ void doSomething(); } interface class ServiceB{ void doSomething(); } class context{ /* 是否有其他服务执行失败 */ public boolean hasSomeServiceError(){} }
上面的实现,主要是解耦了服务间的强依赖调用使得程序可扩展性更好。
- 2,业务B-不同场景切换不同实现。
在该业务下,有多个场景,每个场景需要不同的逻辑。一般的思路是有多个场景,我们就定义多个实现,然后再一个服务方法中,通过不同的场景去选择不同的实现。用伪代码描述如下:
publis class Service{ public void invoke(type type){ switch(type){ // 场景为aType case aType: // 获取到aType的服务发起调用 getAtypeService().invoke(); break; } } }
但是这样带来的问题在于,你每次添加一个场景都需要去写一个获取该type对应服务的方法,并且到主方法里面去发起对该场景type的调用。这样子做是可以的,但是它增加了开发的复杂度。像这种频繁的场景切换对应不同的实现,我们想一想是否有遇到过类似的需求再其他地方有实现我们可借鉴呢?这个貌似我没有遇到过,但是我知道一个相似的理论:通道与管道。通道负责下达执行命令的指令,管道负责将命令送达到执行的地方。基于此我们可以将最外层的调用抽象层通道,里面不同的实现抽象为不同的管道。然后每个管道都有自己的标识,我们通过命令选择对应的管道去执行。优化后的代码结构:
public class ServiceChannel{ public void invoke(Type type){ // 发起调用 findPipeLine(type).invoke(); } private ServicePipeline findPipeLine(Type type){ // 找对应的管道,一般我们要求实现管道接口的类,都要注入到Spring上下文环境中 // 再这里我们可通过spring上下文获取到所有管道 } /** * 对于不同的场景我们只需要实现这个接口,注入到Spring上下文环境,无需其他任何操作,就可实现不同场景不同服务调用了 */ public static SerivcePipeline{ // 发起调用 void invoke(); // 管道类型 Tyep tyep(); } }
上面的实现主要将一些不必要的逻辑对用户进行屏蔽,以及解耦实现与调用的关联。
四、总结
归根结底,一份好的代码结构、必定是要考虑代码隔离、解耦等多方面的。对于这方面确实有待加强、后续看一下有关这方面的理论知识还是很有必要的