其他分享
首页 > 其他分享> > 从HTTL模板引擎看软件设计原则

从HTTL模板引擎看软件设计原则

作者:互联网

HTTL (Hyper-Text Template Language) 是一个高性能的开源JAVA模板引擎, 适用于动态HTML页面输出, 可替代JSP页面, 指令和Velocity相似。作者是阿里巴巴工程师梁飞,本文是在拜读了HTTL的设计原则之后提炼出的部分通用设计原则。

模型划分原则

按实体域,服务域,会话域划分。

不管你做一个什么产品,都一定有一个被操作的主体,比如:服务框架管理的Service,任务框架管理的Task,Spring管理的Bean等,这就是实体域。

即然有被操作者,就一定有操作者,它管理被操作者的生命周期,发起动作,比如:服务框架的ServiceInvoker,,任务框架的TaskScheduler,Spring的BeanFactory等,这就是服务域。

服务域发起动作,在执行过程中,会有一些临时状态需要存储交换,比如:Invacation,Execution,Request等,这就是会话域。

相应的,在HTTL中:

这样划分的好处是,职责清晰,可变状态集中,每个域都是无锁线程安全的,保证在大并发下,不会降低系统的活性。

这些核心领域模型也就是HTTL的API(Application Programming Interface),它是HTTL暴露给用户的最少概念,也就是上面类图中的第一列。

扩展点组装原则

按“微核+插件”体系组装。

但凡有生命力的产品,都是在扩展性方面设计的比较好的,因为没有哪个产品可以覆盖所有需求,对于开源软件尤其如此。

所以,产品只有具有良好的扩展性,允许用户或第三方参与进来,进行二次开发,才能保持生命力。

怎么样的扩展性才是最好的?通常来讲,就是没有任何功能是硬编码的,所有的功能都可被用户替换。

那要如何才能做到这样?一个重要的原则就是:平等对待第三方。

也就是凡是原作者能实现的功能,第三方也要能够在不改变源代码的前提下实现。

换言之,原作者应把自己也当作扩展者,自己添加功能时,也要用第三方扩展者同样的方式进行,而不要有特权。

要做到这一点,就需要一个良好的框架支撑,“微核+插件”是一个不错的选择,Eclipse, Maven等知名软件都采用该体系。

什么是“微核+插件”?微核,即最小化核心,内核只负责插件的组装,不带任何功能逻辑,所有功能都由可替换的插件实现,

并且,组装过程应基于统一的规则,比如基于setter注入,而不能对不同插件硬编码组装,这样可以确保没有任何功能在内核中硬编码。

比如:Spring, OSGI, JMX, ServiceLoader等都是常见的微核容器,它们负责基于统一规则的组装,但不带功能逻辑。

当然,如果你不想带这么重的框架,也可以自行实现,HTTL就采用自行实现的httl.util.BeanFactory作为组装微核。

在Engine.getEngine()中调用了BeanFatory.createBean(Engine.class, properties),

其中,properties即为httl.properties配置,BeanFatory基于setter方法,递归注入所有对象的属性。

比如:httl.properties中配置了parser=httl.spi.parsers.CommentParser,

而DefaultEngine中有setParser(Parser parser)方法,就会被注入,并且Parser本身的属性也会递归注入。

如果你需要扩展或替换HTTL的实现,请参见:扩展集成

既然非功能性的插件组装过程,可以由微核框架来完成,那功能性的组装怎么办呢?

我们应该把功能性的组装过程也封装成插件,即让大插件组装小插件,形成级联组装关系。

比如,HTTL的入口类Engine的实例也是一个插件,它负责模板的缓存,加载,解析的总调度,即你可以替换DefaultEngine实现。

只需在httl.properties中配置:engine=com.your.YourEngine,可以将现有Parser等SPI注入你的Engine。

这些插件的接口,也就是HTTL的SPI(Service Provider Interface),它是HTTL暴露给扩展者的最小粒度的替换单元,也就是上面类图中的第二列。

整体分包原则

按复用度,抽象度,稳定度分包。

稳定度与抽象度关系如下图:

也就是分包应该如下:

其中上面那个包不依赖其它包。所以它很稳定,应尽量把抽象类或接口放在这一层,

而下面那个包依赖了三个包,三个包变化都会引起它跟随变化,所以它是不稳定的,应尽量把具体实现类放在这一层。

因稳定度与抽象度成正比,所以不稳定度与抽象度成反比,用反比方便画图,计算方式如下:

应该保持偏差越小越好,即下图所示交点都落在绿色反比线左右:

基于上面的原则,HTTL的包结构整体上划分为三层:(对应上面类图中的三列)

采用子包依赖父包风格,所以将API放在根目录,SPI接口独立子包,各种实现放在SPI的下一级子包中。

下图是HTTL所有包的不稳定度与抽象度的比值距阵:(下图为JDepend绘制)

HTTL所有核心包都是靠近反比线的,即上图中用绿色标识的点,表示分包是合理的。

注:图中黑色的点为util相关包,它们不抽象,却被很多包依赖,只是内部复用代码,不影响整体设计,用户请不要依赖HTTL的util类。

标签:插件,依赖,软件设计,组装,稳定度,HTTL,模板
来源: https://www.cnblogs.com/yueshutong/p/10418226.html