1、API与SPI分离,API面向使用者,SPI面向扩展者
2、服务域、实体域、会话域分离
-
实体域 框架或组件,总会有核心领域模型,比如Spring的bean,Dubbo的service等,核心领域模型
及其组成部分为实体域,代表我们要操作的目标本身。实体域通常是线程安全的,不管是通过
不变类、同步状态、复制。 -
服务域 也称为行为域,是组件的功能集,同时也负责实体域和会话域的生命周期管理。比如Spring的
ApplicationConext,Dubbo的ServiceManager。 服务域的对象通常比较重,而且是线程安全,以单一实例
服务于所有调用。 -
会话域 会话重重要的概念是上下文,上下文通常持有交互国产重的状态变量等,会话对象通常较轻,
每次请求都重新创建实例,请求结束后销毁
概况之,把元信息交由实体域持有,把一次请求的临时状态由会话域持有,由服务域贯穿整个过程
3、在重要的过程上设计拦截接口
如果是远程调用框架,那远程调用的过程应该有一个统一的拦截接口。如果是ORM框架,那至少SQL执行过程,
Mapping过程要有拦截接口。如果是Web框架,那请求的执行过程应该要有拦截接口。允许外置行为,是框架的
基本扩展方式。拦截接口,通常是把过程本身用一个对象封装起来,传给拦截器链。 比如:远程调用过程为
invoke(),那拦截器接口通常为invoke(Invocation),Invocation对象封装了本来要执行过程的上下文,
并且Invocation里有一个invoke()方法,由拦截器决定什么时候执行,同时,Invocation也代表拦截器
行为本身,这样上一个拦截器的Invocation其实是包装的下一拦截器的过程,知道最后一个拦截器Invocation
是包装的最终invoke()过程
4、重要的状态变更发送事件并留出监听接口
事件和拦截器的区别 拦截器是干预过程的,是过程的一部分,基于过程行为的。事件是基于状态数据的,
任何行为改变的相同状态,对事件应该是是一致的。事件通常是事后通知,是一个Callback接口,方法名通常
是过去式的,比如onChanged()。比如远程调用框架,当网络断开或连上应该发出一个事件,当出现错误也考虑
发出一个事件,这样便于外围应用观察到框架内部的变化,做出选择。
5、扩展接口职责尽可能单一,具有可组合性
比如,远程调用框架协议是可以替换的,如果只提供一个总的扩展接口,当然可以做到切换协议,但协议支持
细分为底层通讯、序列化、动态代理等。如果将接口拆细,正交分解,这样更便于扩展者复用已有逻辑,而只是
替换某部分实现策略,分解的粒度要把握好
6、微核插件式,平等对待第三方
Spring的微核是BeanFactory,通常核心是不该带有功能性的,而是一个生命周期和集成容器,这样各功能
可以通过相同的方式交互扩展,并且任何功能都可以被替换,如果做不到微核,至少要平等对待第三方,即
原作者能实现的功能,扩展者应该可以通过扩展的方式全部做到
7、不要控制外部对象的生命周期
框架最多提供工具类辅助管理,而不是绝对控制
8、可配置一定可编程,并保持友好的COC约定
使用环境的不确定因素很多,框架总会有一些配置,一般都会到classpath扫描某个
指定名称的配置,或者启动时允许指定配置路径。一个通用框架,要做到凡是能配置
文件做的一定要能通过编程方式进行,否则当使用者需要将框架与另一个框架集成时
会带来很多麻烦。另外,尽可能做一个标准约定,如果用户按某种约定做事,就不要
该配置项
9、区分命令与查询,明确前置条件与后置条件
这是契约式设计的一部分,尽量遵守有返回值的方法查询方法,void返回的方法是命令,
查询方法通常是幂等性的。另外,每个方法都尽量前置断言传入参数的合法性,后置断言
返回结果的合法性,并文档化