最近给团队新人讲了一些设计上的常识,可能会对其它的新人也有些帮助,把暂时想到的几条,先记在这里。
1. API与SPI分离
框架或组件通常有两类客户,一个是使用者,一个是扩展者,API(Application Programming Interface)是给使用者用的,而SPI(Service Provide Interface)是给扩展者用的,在设计时,尽量把它们隔离开,而不要混在一起,也就是说,使用者是看不到扩展者写的实现的,比如:一个Web框架,它有一个API接口叫Action,里面有个execute()方法,是给使用者用来写业务逻辑的,然后,Web框架有一个SPI接口给扩展者控制输出方式,如用velocity模板输出还是用json输出等,如果这个Web框架使用一个都继承Action的VelocityAction和一个JsonAction做为扩展方式,要用velocity模板输出的就继承VelocityAction,要用json输出的就继承JsonAction,这就是API和SPI没有分离的反面例子,SPI接口混在了API接口中,合理的方式是,有一个单独的Renderer接口,有VelocityRenderer和JsonRenderer实现,Web框架将Action的输出转交给Renderer接口做渲染输出。
2. 服务域/实体域/会话域分离
任何框架或组件,总会有核心领域模型,比如:Spring的Bean,Struts的Action,Dubbo的Service,Napoli的Queue等等。这个核心领域模型及其组成部分称为实体域,它代表着我们要操作的目标本身,实体域通常是线程安全的,不管是通过不变类,同步状态,或复制的方式,服务域也就是行为域,它是组件的功能集,同时也负责实体域和会话域的生命周期管理,比如Spring的ApplicationContext,Dubbo的ServiceManager等,服务域的对象通常会比较重,而且是线程安全的,并以单一实例服务于所有调用,什么是会话?就是一次交互过程,会话中重要的概念是上下文,什么是上下文?比如我们说:“老地方见”,这里的“老地方”就是上下文信息,为什么说“老地方”对方会知道,因为我们前面定义了“老地方”的具体内容,所以说,上下文通常持有交互过程中的状态变量等,会话对象通常较轻,每次请求都重新创建实例,请求结束后销毁。
简而言之:把元信息交由实体域持有,把一次请求中的临时状态由会话域持有,由服务域贯穿整个过程。