OEA中的AutoUI重构(1) - Command自动生成

    OEA框架的核心之一是AutoUI,其职责是面向领域模型及UI元模型进行生成统一的界面。

    在本次的迭代开发中,需要对命令按钮的生成方式进行一些定制。由于原来并没有为这样的需求留有特别的扩展点,加之原来的生成代码是过程式的代码、且也变得比较冗长,所以我们决定对这一部分的代码进行重构。

原来的模式

    历史代码中,为某一实体类生成命令按钮的流程是这样的:

  1. 找到实体类可用的所有命令按钮元数据。
  2. 对它们进行过滤,依靠权限、版本的客户化元信息等。
  3. 构造几个生成控件的List容器,分别是:itemsInToolbar,itemsInContextMenu,itemsInGroup。
  4. 遍历所有的命令按钮,根据其对应的元数据,分别生成相应的控件(按钮、菜单等),然后添加到容器中。 其中,还有对某些命令的特殊生成处理。例如,为了给命令生成一个附带的文本框,特别添加了IParameterizedCommand接口,实现这个接口的命令,则会调用它自己的控件生成方法来生成控件。应用开发时,扩展的命令需要实现自己的控件生成方案。
  5. 对同一容器中的命令控件进行排序和一些其它的操作。
  6. 把容器中的每一项添加到界面中。

    由于功能是一点一点加进来的,整个代码是过程化的,冗长而不易维护。扩展起来也比较不便。原来只能实现IParameterizedCommand接口并自行生成文本控件,要在总体上控制整个生成流程也只能修改上面的流程中对应的代码,最终只会导致代码膨胀得无法维护。

草稿

    重构不是重做。所以我们不是全部推翻重做,而只是把流程进行优化,并进行职责划分,用适当的对象来承担对应的职责,让类与类之间的协作来完成整个流程。

    首先,整个流程中比较重要的是控件的生成和分组。在进行OO设计时,先要对这部分进行抽象。先看看原来的生成的ToolBar的一张图:

(样式没有做,不好看,哈哈。)图中,主要有三类:分组下拉、带文本的按钮、一般的按钮,当然还应该包含右键菜单中的菜单项。再加上这些迭代的新的样式,画出了下面的草稿:

图中,主要的GroupGenerator的职责是对一组命令进行控件生成,一般情况下一组命令可以生成SplitButtons、GroupTextbox。当一个组中只有一个命令时,它就变为了特殊的ItemGenerator,当个命令可以生成:文本按钮、一般按钮、菜单等。而如何把所有的命令进行分组并生成GroupGenerator,就是GroupingAlgorithm的职责。

详细设计

    (以下内容中涉及具体的OEA的类的职责,用于项目组内沟通,不关心的朋友可以直接跳过细节描述。)

    最后的具体设计方案中,分为以下几下主要的部分:

  1. CommandAutoUIContext:这是对整个生成环境的抽象。
  2. CommandAutoUI:这里面包含了整个生成的流程。
  3. Generators:这里面包含了所有的框架内置的命令组生成方案。
  4. GroupAlgorithms:这里用于把命令进行分组,并为命令组分配生成器。

以下,详细说明每个部分的设计:

CommandAutoUIContext

CommandAutoUIContext 表示生成流程的上下文对象。它比较简单,只是包含了整个生成流程中需要用到的参数,这些参数包含:需要生成命令的实体的元数据信息、可用的ToolBar对象、可用的菜单、所有实体包含的命令、命令要用到的参数。

CommandAutoUIComponent 类表示整个生成流程中的可用的组件,这些组件都可以直接获取上下文对象中的内容。

CommandAutoUI

这里包含整个生成的流程所用到的核心对象:

CommandGroup 表示一个命令组,其中有组名。

GroupGenerator 是一个命令组的生成器,这里为它分配了以下职责:为命令组生成控件、把控件添加到上下文中。

GeneratableGroup 整合了上述两个对象,表示一个可生我生成的命令组。

GroupAlgorithm 表示某个命令的抽象的分组算法。注意,它只负责对某个命令进行分组。

GroupOperation 作为分组操作的执行者,调用分组算法对所有命令进行分组。它负责对所有的命令进行分组。

以上对象作为生成流程的核心对象,被CommandAutoUIManager进行组织并完成最终的界面生成:

GroupGenerators

图中列出了框架内置的可能用到的所有生成器。当然了,要扩展界面生成时,只需要编写新的子类就行了。

具体的内容在前面的“草稿”已经有所描述,在此不再赘述。

GroupAlgorithms

GroupAlgorithm 是策略模式的应用。

框架中内置三个分组算法:

DropDownListAlgorithm 表示把某一个命令分组到下拉组中。

DefaultAlgorithm 表示默认的一个命令一组的分组算法。

GenericItemAlgorithm 表示指定新的生成方式的分组算法。(添加这个类,是因为扩展时,80%以上情况只是为命令更换一种生成显示界面而已。)

整个流程用对象描述如下:

其它相关改动

    IParametrizedCommand 由原来的只支持 String 的 附加文本框命令变为更抽象的“需要参数的命令”。界面生成的控件在用户“填入”数据时,会组装出其所需要的参数,然后对它进行回调:

/// <summary>
/// 一种需要其它参数才能执行的命令。
/// </summary>
public interface IParametrizedCommand : ICommand
{
    /// <summary>
    /// 界面中输入的参数改变时,会通知这个Command参数值改变了。
    /// </summary>
    /// <param name="value"></param>
    void NotifyParameterChanged(object value);
}

总结

    这里再总结一个小的经验:

在开发过程中,为了简化代码,曾尝试使用设置属性的方式来设计构造函数的必要参数。但是属性设置并不是必需的,一旦忘记,则会需要调试才能找到问题所在,得不偿失。也就是说:

  • 不要以为类少就认为自己能记住其中的设置约定。最好是让编译器提醒你。 :)
  • 不要因为简化代码而去尝试违反一些好的设计规范。否则,不但可能写出不易调试的代码,而且影响后人的阅读。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏hbbliyong

openFileDialog的Filter属性设置

OpenFileDialog对话框的Filter属性说明:          首先说明一个示例,分析一下Filter属性的构成:“ Excel文件|*.xls ...

29170
来自专栏计算机编程

Vue 单文件组件详解<1>--简单上手

vue 的webpack的结构非常简单,简单的一眼就可以知道每个目录下是干啥的,在components目录下就有属于我们想要详细解析的内容,这两个vue文件即为...

10810
来自专栏为数不多的Android技巧

Android Studio你不知道的快捷键(三)

有没有这样的场景:你在Android Studio打开了一个图片文件(或者别的文件),想在资源浏览器里面查看这图片;在Eclipse里面我想大部分的人是Alt ...

13910
来自专栏河湾欢儿的专栏

sass

我们大家都知道html、css不属于编程语言属于标记语言,所以很难像js一样定义变量、编写方法实现模块化,而目前的css编写模式中都是定义一些公共样式类名,那一...

35910
来自专栏挖坑填坑

angular页面打印局部功能实现方法思考

在页面显示的时候是分页现实的,当前页面只有10条数据,但是打印需要打印完整的100条数据。 并且在当前页面包含了表格之外的标题,菜单等其他元素。 后天api...

12620
来自专栏葡萄城控件技术团队

Webpack4教程 - 第二部分,使用loader处理scss,图片以及转换JS

今天继续我们的Webpack 4入门教程。在介绍了Webpack的基本概念之后,是时候更深入一点了。这次我们会涉及Webpack中非常强大的一个东西:loade...

19210
来自专栏Objective-C

iOS-UITableView 之 重写 loadView 导致程序崩溃

29160
来自专栏小狼的世界

Apache环境下页面乱码的几种可能总结

采用典型的LAMP架构开发的时候,环境中多处涉及到编码的指定,有一个地方忽略,都有可能造成页面汉字乱码的产生,本文将总结这些乱码产生的可能的原因,方便我们排查。

11210
来自专栏Google Dart

AngularDart Material Design 选项树 顶

如果SelectionOptions实现Parent接口,则为Parent.hasChildren设置的每个选项显示一个handle,并且切换handle将从P...

20120
来自专栏ionic3+

【Appetite】ionic3实录(七)次页实现及分析解决问题【下】

一般一些js插件,是依托dom的。我们观察initSwiper方法,第一个参数'.wheel .swiper-container'其实是个选择器,所以它也是依托...

8820

扫码关注云+社区

领取腾讯云代金券