聊聊视图层切面实现方案

我的未来我决定 提交于 2020-01-07 07:20:09

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

前面发过一篇《谈谈应用层切面设计》@HulkZ说看了好几遍也没看懂,然后我又拉他到小黑屋面壁思过了好几次;也有人关心个性与扩展性如何得到平衡;也有人说,悠然就会扯淡,如何实现呢?那么今天继续来扯扯淡看看如何实现。

需求由来

  • 在页面的某一个固定区域内,根据配置动态的增加新的功能。
  • 新增功能元素可以增量添加,不影响原有功能,无时序问题。
  • 支持模块化,自动加载配置。
  • 可以自定义功能元素展现顺序。
  • 满足各种功能模块的数据调用。

##实现思路 为了实现上述需求我们需要在前端页面上设计一种扩展机制,就像在页面上埋下一个“桩子”,一个个功能在需要的时候就往里放,界面上就加载这些功能;当不需要的时候,从里面拿出来,当然需要做到各个功能模块是高内聚低耦合。

从平台的角度上来说就是可以通过配置的方式往某个扩展点不断的添加新内容。而这个扩展点里的内容是有由各个功能模块组合而成,在加载显示的时候根据配置显示的顺序而加载。各个功能模块上高内聚,页面风格各自保持一致。

扩展点的配置可以放到XML文件中,各个功能模块有自己的配置文件,在框架启动过程中自动整合加载相关的配置文件,从而实现自动装配,同时也实现了增量添加功能模块。

在扩展点的调用要做到输入参数的统一调度,可以把公用的参数提到接口上,同时也要兼顾一些功能点的额外需求,因此一个Map参数是不可或缺的。

##实现关键 实现方法多种多样,这里以使用tiny平台为例。 在页面上埋坑也有多种方式,这里通过调用模板语言宏来实现,说到模板语言不得不用用我家的tiny模板语言。 定义切点宏,也就是上文所说的扩展点:

#macro extensionPoint(type objectType objectId foundationMap)

    ##获取扩展点内容
    #set(text = extensionPointManager.getExtensionPointMacroByType(type))

    ##解析扩展点内容
    ${eval(text)}

#end

从上面的代码可以出扩展点调用时,需要声明扩展点类型type,业务对象类型objectType和业务对象标识符objectId。这几个参数都是通用参数因此显示的声明,对于不同功能模块需要的额外参数通通放进foundationMap里面。而对于这个宏的实体内容实际上是通过一个扩展点管理器获取各个功能的模板宏,然后再调用eval来解析各宏的内容。

扩展点的定义是声明在XML中,格式如下:

<extension-points>
    <extension-point type="扩展点类型" order="排序" name="扩展点名称">
        扩展点内容
    </extension-point>    
</extension-points>

扩展点的定义可以分散在各个模块各自不同的文件。各扩展点由框架的文件搜索器fileResolver根据配置文件后缀*.ext-point.xml进行搜索动态加载。 对平台中扩展点的整合是通过ExtensionPointManager进行管理,他的作用包括读取XML后存储扩展点信息、获取扩展点信息、删除扩展点等等。

public interface ExtensionPointManager {
    String XSTREAN_PACKAGE_NAME = "extensions";

    /**
     * 添加扩展点
     *
     * @param extensionPoint
     */
    void addExtensionPoint(ExtensionPoint extensionPoint) throws ExtensionPointException;

    /**
     * 添加扩展点
     *
     * @param extensionPoints
     */
    void addExtensionPoints(ExtensionPoints extensionPoints) throws ExtensionPointException;

    /**
     * 移除扩展点
     *
     * @param extensionPoint
     */
    void removeExtensionPoint(ExtensionPoint extensionPoint);

    /**
     * 移除扩展点
     *
     * @param extensionPoints
     */
    void removeExtensionPoints(ExtensionPoints extensionPoints);

    /**
     * 根据扩展类型查找所有符合的扩展点内容
     *
     * @param type
     * @return
     */
    String getExtensionPointMacroByType(String type);

    /**
     * 根据类型查找扩展点,排除不需要的扩展点名称
     *
     * @param type
     * @param excludeNames 多个排除扩展点名称以","分隔
     * @return
     */
    String getExcludeExtensionPointMacroByType(String type, String excludeNames);

    /**
     * 根据扩展点名称获取宏
     *
     * @param names
     * @return
     */
    String getExtensionPointMacroByNames(String names);
}

开发实例

基于应用切面我们已经做了一大批基本业务切面组件——foundation,它的诞生是为了给具体项目的快速开发提供基础的业务组件,并且以应用切面的方式做良好的插拔式使用,提高开发效率,同时是对资产的积累和重复利用。目前已经开发完成二十余个业务组件。并且在项目KMS(http://kms.tinygroup.org)中进行实践检验。

KMS是一个知识管理器系统,而对于这个系而言,他真正开发的工作只专注于他的自己的业务,就是知识管理,这时具体的项目开发就非常的简单。但具体项目开发简单并不意味着这个项目的最后呈现的功能效果简单。实际上他还包括对一篇文档的点赞、收藏、关注、评论、浏览信息记录、关联文档、历史版本管理等等一些列复杂的功能。而这些功能具体的项目中无需二次开发,只需依赖foundation提供的应用切面即可完成功能多样的扩展功能,而作为项目本身只需要关注自己应该专注的业务就好。

KMS效果 在这个界面中由于在不同的位置分布需要灵活的扩展点,因此埋下了两个桩,也就是调用了两次扩展点。

#extensionPoint("docMenu" "doc" doc?.docId)

一个是扩展类型为docMenu的扩展点,位于页面的上部,用于展现此片文章的浏览信息,关注、收藏、点赞等信息功能点。对象类型为doc文档的标识通过一个变量doc.docId传入。 每个扩展点的具体定义配置在如下的XML文件中,每个扩展点又对应着具体的宏,当然如果没有二次封装的必要也可以直接写模板语言具体的实现内容。

<extension-points>
    <!--浏览信息-->
    <extension-point type="docMenu" order="1" name="visitInfo">
        #visitInfo(objectId objectType foundationMap)
    </extension-point>

    <!--点赞信息-->
    <extension-point type="docMenu" order="2" name="goodInfo">
        #goodInfo(objectId objectType foundationMap)
    </extension-point>

    <!--踩信息-->
    <extension-point type="docMenu" order="3" name="badInfo">
        #badInfo(objectId objectType foundationMap)
    </extension-point>

    <!--收藏信息-->
    <extension-point type="docMenu" order="4" name="favoritesInfo">
        #favoritesInfo(objectId objectType foundationMap)
    </extension-point>

    <!--关注信息-->
    <extension-point type="docMenu" order="5" name="followInfo">
        #followInfo(objectId objectType foundationMap)
    </extension-point>

    <!--评论信息-->
    <extension-point type="docMenu" order="6" name="commentInfo">
        #commentInfo(objectId objectType foundationMap)
    </extension-point>

</extension-points>

另一个扩展点类型docBottom的扩展点,位于页面底部,可以动态添加多个附加功能模块。

#extensionPoint("docBottom" "doc" doc?.docId {"spaceId":spaceId,"docExt":docText,"status":status})

类型为docBottom的扩展点包括显示这篇文档对于的tag和关联信息及强大的评论功能

<extension-points>
    <!--标签扩展-->
    <extension-point type="docBottom" order="1" name="tagPannel">
        #tagPannel(objectId objectType foundationMap)
    </extension-point>    
    <!--关联扩展-->
    <extension-point type="docBottom" order="2" name="relationPannel">
        #relationPannel(objectId objectType foundationMap)
    </extension-point>    
    <!--评论扩展-->
    <extension-point type="docBottom" order="3" name="commentPannel">
        #commentPannel(objectId objectType foundationMap)
    </extension-point>    
</extension-points>

##总结 通过上述设计,我们初步实现了页面层上的切面设计。如果在具体功能模块设计可以真正的做到高内聚、松耦合,那么就可能就是我们想要的业务切面。

页面切面让人最直接的感受的是,新增的功能模块在页面上可以通过配置文件的配置增量添加,对于已有的功能模块可以通过卸载部分程序包拆卸对应功能模块,这样的用户体验是不是很酸爽呢?如果模块化做到了极致,再配上自动装配的黑科技,也许你也可以!

关于业务切面其实还有业务层、控制层一些特殊设计,并且其中夹杂一些关键小步骤,需要用心体会和设计,后面有时间的时候或许也会介绍。

有喜欢本人博客内容的同学请加关注,以便及时获知本人精品内容!

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!