前言
影城客户端从16年底开始设计到现在都过去快两年了,这里我做一个简单的回顾。
技术选型
NativeUI:性能最高,开发难度最大,代表产品QQ和微信,没有基因没有技术栈。
Electron+H5:不支持xp,使用xp的电脑在影城中还存在50%以上,只能否决。
NWJS+H5:总体上比Electron差一些,但是支持xp,因为存在性能,操作体验,安装包庞大等问题,被钉钉弃用。
NativeUI+CEF+H5:hybird框架,将性能要求高的UI用C++写,变化频繁的用h5写,钉钉客户端最新采用的技术框架。网易云音乐客户端也是类似框架。也没有技术栈。
WinForm:.Net平台下的两大UI框架之一,具有开发简单,轻量,性能高等优点,但是有自定义UI难度高,微软停止更新等缺点。
WPF:.Net平台下的两大UI框架之一,具有现代化的UI界面和比较先进的MVVM编程思想。公司上代产品使用的技术,具有广泛的实践经验,读卡和打印等类库可以直接继承使用。这是最不容易采坑的技术方案。
当时公司的战略是用迅雷不及掩耳之势打下云售票这片新市场,一边要和时间赛跑,一边要保证产品质量和功能,需要开发节奏要快,狠,准,所以综上所述,最终沿用WPF技术开发新产品。
一个优秀系统架构应该是具有高可扩展性、高内聚、低耦合等特点,在经历了各版本的变更之后依然保持着清晰、灵活、稳定的系统架构。而模块化是其中一个解决方案,为了以后框架的灵活扩展和应付日益复杂的业务,模块化应该是新产品的设计方向。
业务模块
POS是给前台售票员使用的系统,业务上相对简单,大部分都是与顾客相关,可粗略分为影票,卖品,会员三大类,还有打印,读卡等硬件交互模块,另外还有登录,客屏这些功能,抽钞,赠券等归类为其他业务。
业务模块再细化,系统架构图雏形
上面粗粒度的业务分类还是难以支持系统设计,还是需要进一步的细化。在系统架构层面就是要业务模块在细化,和补全必要的组件,例如日志,数据服务等。
完全解耦的平台化
最开始想的方案是完全平台化,整个系统的核心只是作为启动器的exe,其他模块dll作为插件都可以替换的,再通过读取到内存,以反射方式加载,可以实现各个模块的热插拔。启动器和插件的关系就好像浏览器和H5页面。
这个方案可以使各个模块完全解耦,从编译的角度来说完全独立。但缺点很明显,代码冗余大,日志,打印等功能基本都一样的,修改和维护相当困难。另外各模块之间的业务都是有依赖和关联的,他们之间的通信可以采用字符串传递,再反序列得到实例。事实上这种靠口头约定的通信方式出现一丁点的误差就会造成严重的后果。综上,这种方案并不适合。不过可以作为一个局部平台,对外接收各种第三方开发的插件实现业务功能。
改进方案
上面灾难性的字符串通信改成各模块引用dll,实现强类型通信。作为一个UI系统需要用到各个控件,也需要做一个通用类库给各模块引用。另外,为了国际化的需要和主题更换的实现,文案资源和样式也需要独立出来。
具体实现
具体到代码实现层面,考虑到安装包的大小和第三方框架的兼容性,我决定以轻量化作为设计抉择的方向
IOC容器
上面有各式各样模块为了方便管理各个组件,Bootstrapper需要作为一个容器,担任加载和管理组件。
微软集成在.Net 4.0框架中,不需要引入第三方框架,符合客户端轻量化的要求。
可根据路径扫描和加载组件。
可以卸载和重新组合组件。
以MEF框架为基础,我做了几样工作。
定义了CataLog.xml配置文件,里面记录着每个组件的实际文件路径,在程序启动时,读取配置加载需要的组件。
设计IModule接口,如果组件需要在运行时执行任务,可以实现接口的Initialize方法。
封装MEF的容器为全局容器Global.Container,可通过容器导出其他组件的服务。
改进MEF的加载组件的方式,先把组件读取到内存的方式,再转化成Assembly加载,可以实现运行时卸载组件,更新组件。
MVVM框架
MVVM是Model-View-ViewModel的简写,是一种比较先进的设计模式,WPF本身就是一个实现MVVM模式的框架,主张数据驱动理念,但是它里面对命令,属性通知等概念的实现不是很友好,所以我找了行业上比较成熟的第三方框架做了一番对比。
MvvmLight, 是一个轻量级的MVVM框架,比较适合小程序开发,对命令,属性通知接口等有相应的实现,使用Mesage类作为消息传递。使用SimpleIoc容器。
Prism,相对于来庞然大物,适合大规模程序开发,支持模块化,组合UI,组合命令,事件聚集,支持Unity和MEF作为IOC容器。使用上非常复杂,而实际开发上很少用得上全部功能。
最后,走读MvvmLight和Prism文档和源码,取两者之长构建自己的框架。参考MvvmLight将命令,属性通知等功能在Infrastructure中实现,参考Prism的模块化思想,以接口IMdoule作为模块间加载和交互的桥梁。
控件库
为了风格统一和代码复用,需要一个控件库来开发。但是原生控件样式简陋功能少,而第三方控件比较好有ModernUI,Xceed和Telerik,前者开源,扁平化风格,功能上和原生差不多,后两者部分开源,高度封装,功能强大,不过要收费。实际上,按照视觉搞,原生和第三方控件都是不符合要求,最终还是要自建LarkUI控件库,重写样式和功能以满足视觉和交互的要求,最终集成在Infrastructure。
自动更新
新产品的迭代是非常频繁的,为了防止版本碎片化,POS需要实现强制更新,另外频繁的更新会降低用户体验,所以必须要自动更新且尽量无感,启动时检查,下载更新包替换文件后重启。
小结
本文简单介绍了客户端的架构,总的来说,在行业上WPF框架并没有一个标准的解决方案,也可以抽象出来作为一个WPF的标准客户端架构输出到行业去。