最近给团队新人讲了一些设计上的常识,可能会对其它的新人也有些帮助, 把暂时想到的几条,先记在这里。数据库
一、API与SPI分离编程
框架或组件一般有两类客户,一个是使用者,一个是扩展者。 API(Application Programming Interface)是给使用者用的, 而SPI(Service Provide Interface)是给扩展者用的。 在设计时,尽可能把它们隔离开,而不要混在一块儿, 也就是说,使用者是看不到扩展者写的实现的。json
好比:数组
SPI接口混在了API接口中,合理的方式是,有一个单独的Renderer接口,有VelocityRenderer和JsonRenderer实现, Web框架将Action的输出转交给Renderer接口作渲染输出。安全
反正例子:bash
正确例子:网络
二、服务域/实体域/会话域分离架构
任何框架或组件,总会有核心领域模型,好比:app
实体域:像Spring的Bean,Struts的Action,Dubbo的Service,Napoli的Queue等等 。这个核心领域模型及其组成部分称为实体域,它表明着咱们要操做的目标自己, 实体域一般是线程安全的,无论是经过不变类,同步状态,或复制的方式。框架
服务域:也就是行为域,它是组件的功能集,同时也负责实体域和会话域的生命周期管理。好比Spring的ApplicationContext,Dubbo的ServiceManager等, 服务域的对象一般会比较重,并且是线程安全的,并以单一实例服务于全部调用。
会话域:就是一次交互过程, 会话中重要的概念是上下文,什么是上下文? 好比咱们说:“老地方见”,这里的“老地方”就是上下文信息, 为何说“老地方”对方会知道,由于咱们前面定义了“老地方”的具体内容, 因此说,上下文一般持有交互过程当中的状态变量等, 会话对象一般较轻,每次请求都从新建立实例,请求结束后销毁。
简而言之: 把元信息交由实体域持有, 把一次请求中的临时状态由会话域持有, 由服务域贯穿整个过程。
实例一
实例二
两 种 例 子
复制代码
三、在重要的过程上设置拦截接口
1.若是你要写个远程调用框架,那远程调用的过程应该有一个统一的拦截接口;
2.若是你要写一个ORM框架,那至少SQL的执行过程,Mapping过程要有拦截接口;
3.若是你要写一个Web框架,那请求的执行过程应该要有拦截接口;
复制代码
等等,就能够自行完成,而不用侵入框架内部。拦截接口,一般是把过程自己用一个对象封装起来,传给拦截器链。
好比:远程调用主过程为invoke(),那拦截器接口一般为invoke(Invocation),Invocation对象封装了原本要执行过程的上下文,而且Invocation里有一个invoke()方法, 由拦截器决定何时执行。同时,Invocation也表明拦截器行为自己, 这样上一拦截器的Invocation实际上是包装的下一拦截器的过程, 直到最后一个拦截器的Invocation是包装的最终的invoke()过程, 同理,SQL主过程为execute(),那拦截器接口一般为execute(Execution),原理同样, 固然,实现方式能够任意,上面只是举例。
四、重要的状态的变动发送事件并留出监听接口
这里先要讲一个事件和上面拦截器的区别:
拦截器:是干预过程的,它是过程的一部分,是基于过程行为的。 事件:是基于状态数据的,任何行为改变的相同状态,对事件应该是一致的,事件一般是过后通知,是一个Callback接口,方法名一般是过去式的,好比onChanged()。
好比远程调用框架,当网络断开或连上应该发出一个事件,当出现错误也能够考虑发出一个事件, 这样外围应用就有可能观察到框架内部的变化,作相应适应。
五、扩展接口职责尽量单一,具备可组合性
好比,远程调用框架它的协议是能够替换的, 若是只提供一个总的扩展接口,固然能够作到切换协议, 但协议支持是能够细分为底层通信,序列化,动态代理方式等等, 若是将接口拆细,正交分解,会更便于扩展者复用已有逻辑,而只是替换某部分实现策略, 固然这个分解的粒度须要把握好。
六、微核插件式,平等对待第三方
大凡发展的比较好的框架,都遵照微核的理念
Eclipse的微核是OSGi, Spring的微核是BeanFactory,Maven的微核是Plexus。
一般核心是不该该带有功能性的,而是一个生命周期和集成容器, 这样各功能能够经过相同的方式交互及扩展,而且任何功能均可以被替换, 若是作不到微核,至少要平等对待第三方, 即原做者能实现的功能,扩展者应该能够经过扩展的方式所有作到, 原做者要把本身也看成扩展者,这样才能保证框架的可持续性及由内向外的稳定性。
七、不要控制外部对象的生命周期
好比上面说的Action使用接口和Renderer扩展接口, 框架若是让使用者或扩展者把Action或Renderer实现类的类名或类元信息报上来。而后在内部经过反射newInstance()建立一个实例, 这样框架就控制了Action或Renderer实现类的生命周期, Action或Renderer的生老病死,框架都本身作了,外部扩展或集成都无能为力。
好的办法是让使用者或扩展者把Action或Renderer实现类的实例报上来, 框架只是使用这些实例,这些对象是怎么建立的,怎么销毁的,都和框架无关, 框架最多提供工具类辅助管理,而不是绝对控制。
八、可配置必定可编程,并保持友好的CoC约定
由于使用环境的不肯定因素不少,框架总会有一些配置, 通常都会到classpath直扫某个指定名称的配置,或者启动时容许指定配置路径, 作为一个通用框架,应该作到凡是能配置文件作的必定要能经过编程方式进行, 不然当使用者须要将你的框架与另外一个框架集成时就会带来不少没必要要的麻烦。
另外,尽量作一个标准约定,若是用户按某种约定作事时,就不须要该配置项。 好比:配置模板位置,你能够约定,若是放在templates目录下就不用配了, 若是你想换个目录,就配置下。
九、区分命令与查询,明确前置条件与后置条件
这个是契约式设计的一部分,尽可能遵照有返回值的方法是查询方法,void返回的方法是命令, 查询方法一般是幂等性的,无反作用的,也就是不改变任何状态,调n次结果都是同样的。好比get某个属性值,或查询一条数据库记录。
命令是指有反作用的,也就是会修改状态,好比set某个值,或update某条数据库记录, 若是你的方法即作了修改状态的操做,又作了查询返回,若是可能,将其拆成写读分离的两个方法。
好比:
十、增量式扩展,而不要扩充原始核心概念
咱们平台的产品愈来愈多,产品的功能也愈来愈多, 平台的产品为了适应各BU和部门以及产品线的需求。势必会将不少不相干的功能凑在一块儿,客户能够选择性的使用, 为了兼容更多的需求,每一个产品,每一个框架,都在不停的扩展, 而咱们常常会选择一些扩展的扩展方式,也就是将新旧功能扩展成一个通用实现。
我想讨论是,有些状况下也能够考虑增量式的扩展方式,也就是保留原功能的简单性,新功能独立实现。我最近一直作分布式服务框架的开发,就拿咱们项目中的问题开涮吧。
好比:远程调用框架,确定少不了序列化功能,功能很简单,就是把流转成对象,对象转成流, 但因有些地方可能会使用osgi,这样序列化时,IO所在的ClassLoader可能和业务方的ClassLoader是隔离的, 须要将流转换成byte[]数组,而后传给业务方的ClassLoader进行序列化。
为了适应osgi需求,把原来非osgi与osgi的场景扩展了一下, 这样,无论是否是osgi环境,都先将流转成byte[]数组,拷贝一次。然而,大部分场景都用不上osgi,却为osgi付出了代价, 而若是采用增量式扩展方式,非osgi的代码原封不动, 再加一个osgi的实现,要用osgi的时候,直接依赖osgi实现便可。
再好比:最开始,远程服务都是基于接口方法,进行透明化调用的, 这样,扩展接口就是,invoke(Method method, Object[] args), 后来,有了无接口调用的需求,就是没有接口方法也能调用,并将POJO对象都转换成Map表示, 由于Method对象是不能直接new出来的,咱们不自觉选了一个扩展式扩展, 把扩展接口改为了invoke(String methodName, String[] parameterTypes, String returnTypes, Object[] args), 致使无论是否是无接口调用,都得把parameterTypes从Class[]转成String[]。
若是选用增量式扩展,应该是保持原有接口不变, 增长一个GeneralService接口,里面有一个通用的invoke()方法, 和其它正常业务上的接口同样的调用方式,扩展接口也不用变, 只是GeneralServiceImpl的invoke()实现会将收到的调用转给目标接口, 这样就能将新功能增量到旧功能上,并保持原来结构的简单性。
再再好比:无状态消息发送,很简单,序列化一个对象发过去就行, 后来有了同步消息发送需求,须要一个Request/Response进行配对, 采用扩展式扩展,天然想到,无状态消息实际上是一个没有Response的Request, 因此在Request里加一个boolean状态,表示要不要返回Response, 若是再来一个会话消息发送需求,那就再加一个Session交互。而后发现,原来同步消息发送是会话消息的一种特殊状况, 全部场景都传Session,不须要Session的地方无视便可。 若是采用增量式扩展,无状态消息发送原封不动。
同步消息发送,在无状态消息基础上加一个Request/Response处理, 会话消息发送,再加一个SessionRequest/SessionResponse处理。
为何某些人会一直比你优秀,是由于他自己就很优秀还一直在持续努力变得更优秀,而你是否是还在知足于现状心里在窃喜!
我本人邀约各大BATJ架构大牛共创Java架构师社区群,(群号:673043639)致力于免费提供Java架构行业交流平台,经过这个平台让你们相互学习成长,提升技术,让本身的水平进阶一个档次,成功通往Java架构技术大牛或架构师发展
合理利用本身每一分每一秒的时间来学习提高本身,不要再用"没有时间“来掩饰本身思想上的懒惰!趁年轻,使劲拼,给将来的本身一个交代!
To-陌霖Java架构
复制代码
分享互联网最新文章 关注互联网最新发展