软件开发有点类似于艺术品的雕刻,需要不断的打磨。在我们基于一套成熟的架构开发一套软件后,往往都还是需要反复的推敲,进行不断的重构优化,使得重复的代码、硬编码等地方越来越少,也使得某些逻辑越来越有弹性化。软件开发和艺术品又有所不同,艺术品可能很容易完工并封刀,而软件开发却是持续的改进,软件最终的持续改进会变为产品化的归宿,而软件本身可以从很多维度进行不断的优化和重构。
1、多数据库支持的设计
一个良好的产品,可能往往需要支持多种数据库的接入,根据实际业务的需要进行调整,有时候可能需要2到3种数据库的支持。
在Winform框架上,基于微软企业库的底层数据处理,其本身内在支持或者扩展支持多数据库的统一模型接入,因此我们可以在不同的DAL 分层上进行一些扩展的、特定数据库的实现即可。
后来继续开发基于SqlSugar的开发框架的时候,也是希望一个框架支持多种数据库的,而SqlSugar本身它很容易支持接入多种数据库,包括上面的各种类型数据库。
由于SQLSugar的表和字段信息,都是通过特性描述的方式定义的,它们在不同的数据库是通用的,而我们要做的就是在启动的时候,加载不同的数据库连接字符串,进行初始化即可,如下我们定义了基于.netcore的Web API配置信息。
而对于.netFramework的Winform等程序,我们使用App.Config的配置文件来定义。
我们在数据操作的Crud基类里面,通过不同连接字符串的方式,构建不同的数据库处理,因此需要通过工厂方式进行构建,如下所示。
有了这些底层的支持,我们只需要在基类函数里面定义好相关的标准处理:如增、删、改、查、分页、导入、导出、排序等通用接口,以及针对不同业务模块,提供一个接口的扩展和实现即可,如下设计图所示。
针对MongoDB的NoSQL类型数据库,我们可以提供另外一个类似的基类实现封装处理,来简化子类代码。
这样我们同样实现了对多种数据库的支持。
2、多种数据接入方式的处理
除了对不同数据库类型的接入外,我们试着提高一个维度来查看。我们的数据可能来自本地数据库,也可能来自Web API 的接口,还有可能来自WCF服务等其他服务接口。如果他们都实现了相关的接口,那么我们需要使用一个类似开关的方式来切换接受不同的数据处理通道。
例如在我们《Winform混合开发框架》中,支持Web API、WCF数据访问、直接数据访问三种方式接入,可以自由切换。它的设计模式是通过一个开关方式进行切换。
我们以字典模块业务的实现为例,它们在数据库访问方式、Web API访问方式、WCF访问方式,都是基于同一接口进行的不同实现,因此他们的接口关系图可以用下面的方式来表述。
而我们在此基础上开发的《基于SqlSugar的Winform端开发框架》,我们依旧采用这个模式来自由切换本地数据库模式或Web API的访问方式。如下所示。
而对应Web API的代理调用类,那么为了极大的重用常规的接口处理,我们需要类似的继承关系。
在Web API的服务端中,我们为了重用,也是以基类和接口的方式来统一处理相关的逻辑。如我们根据项目的需要,定义了一些Web API控制器的基类,用于实现不同的功能。
而对于 MongoDB的Web API控制器,我们为了方便开发,也设计了同类型的Web API 控制器基类。
同样,BS的前端和移动端,我们根据框架后端的接口进行前端JS端的类的封装处理,引入了ES6类的概念实现业务基类接口的统一封装,简化代码。
对于Python的数据表操作,一样是可以借鉴上面的基类封装方式,降低子类代码的编写,有利于前端后端的相同接口处理。
我们从上面可以看到,不管对于终端的调用(Vue3的TS客户端、WInform端、WPF端、Python前端后端)都是以一定的基类抽象的方式,尽可能的封装相同逻辑接口的实现,通过泛型类的引入,可以非常弹性化接口的实现处理。
3、前端界面的基类抽象
要降低一个程序的编码数量,提高开发效率,很大程度上就是提高基类的通用性和封装逻辑,通过重用具体的实现或者重用一定的逻辑,可以大大降低重复代码的编写,提高代码的可读性和可维护性。
前面介绍了,对于多种数据库,如SqlServer、Mysql、Oracle、Sqlite、PostgreSQL、达梦数据库等的接入,我们把关系型数据库的接入抽象为一个基类,来封装绝大多数的数据处理,而对于NoSQL数据库,可以参考类似的实现独立在处理一个基类。
对于更高一层如WCF、Web API、数据库访问的封装调用,我们可以包装一层来使它们的数据访问透明化,通过开发的方式来决定具体的调用方式。
在Web API实现中,我们也通过对控制器基类统一封装,实现了常规的接口处理和数据的封装调用。
我们这里继续介绍一下基类的重要性,基于前端界面来对基类进行不同的封装处理。
在前端界面中,一般我们把不同的视图窗口归类,有些是列表查询展示的,有些是单条记录展示的,有些则是自定义用户控件元素的,因此他们可以分为几类进行不同的封装。
我们把常规的列表界面,新增、编辑、查看、导入等界面放在一起,除了列表页面,其他内容以弹出层对话框的方式进行处理,如下界面示意所示。
对于Winform界面来说,我们为它们设计了不同的基类,以便一些相同内容的处理。
对于WPF的前端来说,它是基于MVVM的模式的。整个视图模型ViewModel的继承关系如下所示。对于不同的业务类,我们也只需要根据实际情况,生成对应的业务视图模型类即可。
对于WxPython、PySide的跨平台Python前端来说,我们也是依照类似的不同窗体来进行不同的基类封装,提高代码的重用,并降低子对话框或者列表等子类界面元素的复杂度和代码量。
对于一个复杂的界面处理,我们还可以逐步细分他们的一些重复性元素,可以重用,也方便减少模块的代码,降低关注点。
如对于一个机构信息展示内容来说,可能包括了组织成员的信息,角色信息等,我们可以把它们逐一细化,细化的尺度以最大程度的重用代码为核心。
如一个角色的信息,可能包括多个方面,我们把它们按照一定的边界进行划分不同的用户控件,实现重用或者减少一个复杂模块的代码量。
有些模块,如上传文件的处理,我们还可以通过开关配置的方式,实现类似于多数据库接入的那种方式。根据配置信息来确定上传的处理路径选择,就是一种简单的工厂设计模式。
如果我们在业务模块的基础上进行划分,还可以针对不同的业务来封装相关的模块组件,这样可以横向切割一个大的系统为不同的业务模块。
基于Winform,通过数据库或WebAPI数据访问的Winform前端。
基于CommunityToolkit.Mvvm 、lepoco/wpfui 、HandyControl几个基础框架整合的WPF前端界面如下所示。
基于Vue3+ElementPlus+TypeScript的BS前端
基于Python+ WxPython的跨平台前端
基于Python+PySide/PyQt的跨平台前端。