【WPF】影城POS的前世今生
前言
POS从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页面。

这个方案可以使各个模块完全解耦,从编译的角度来说完全独立。但缺点很明显,代码冗余大,日志,打印等功能基本都一样的,修改和维护相当困难。另外各模块之间的业务都是有依赖和关联的,他们之间的通信可以采用字符串传递,再反序列得到实例。事实上这种靠口头约定的通信方式出现一丁点的误差就会造成严重的后果。综上,这种方案并不适合POS。不过可以作为一个局部平台,对外接收各种第三方开发的插件实现业务功能。
改进方案
上面灾难性的字符串通信改成各模块引用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需要实现强制更新,另外频繁的更新会降低用户体验,所以必须要自动更新且尽量无感,启动时检查,下载更新包替换文件后重启。

小结
本文简单介绍了POS的架构,总的来说,在行业上WPF框架并没有一个标准的解决方案,所以整个设计上有比较强烈的个人喜好在里面。未来的话,也可以抽象出来作为一个WPF的标准客户端架构输出到行业去。
【WPF】影城POS的前世今生的更多相关文章
- WPF 基础到企业应用系列2——WPF前世今生
1.开篇前言 非常多时候了解一项新技术的历史和趋势往往比这项技术的本身价值还要重要.WPF作为一项新技术(已经三年多了.或者应该叫老技术了).我们都有必要了解它的来龙去脉,尤其是公司的CT ...
- 重复造轮子系列——基于FastReport设计打印模板实现桌面端WPF套打和商超POS高度自适应小票打印
重复造轮子系列——基于FastReport设计打印模板实现桌面端WPF套打和商超POS高度自适应小票打印 一.引言 桌面端系统经常需要对接各种硬件设备,比如扫描器.读卡器.打印机等. 这里介绍下桌面端 ...
- 第五篇:在SOUI中使用XML布局属性指引(pos, offset, pos2type)
窗口布局的概念 每一个UI都是由大量的界面元素构成的,在Windows编程,这些界面元素的最小单位通常称之为控件. 布局就是这些控件在主界面上的大小及相对位置. 传统的布局一般使用一个4个绝对坐标来定 ...
- WPF基础到企业应用系列6——布局全接触
本文转自:http://knightswarrior.blog.51cto.com/1792698/365351 一. 摘要 首先很高兴这个系列能得到大家的关注和支持,这段时间一直在研究Windows ...
- WPF拖放功能实现zz
写在前面:本文为即兴而作,因此难免有疏漏和词不达意的地方.在这里,非常期望您提供评论,分享您的想法和建议. 这是一篇介绍如何在WPF中实现拖放功能的短文. 首先要读者清楚的一件事情是:拖放主要分为拖放 ...
- 在uwp仿制WPF的Window
移植WPF软件到uwp时碰到用作对话框的Window有多种处理选择.我个人认为最省事的是用ContentDialog模拟Window. 比如你想把上面这个WPF窗体弄到uwp里面去 1.修改Conte ...
- 【转】【WPF】wpf 图片指针处理
我一直用GDI+做Winform 的基于指针的图片处理,这次下决心全部移到wpf上(主要是显示布局很方便)采用的图片是2512*3307 的大图 830万像素类库基于WritableBitmapEx ...
- WPF 基础到企业应用系列索引
转自:http://www.cnblogs.com/zenghongliang/archive/2010/07/09/1774141.html WPF 基础到企业应用系列索引 WPF 基础到企业应用系 ...
- WPF拖动总结[转载]
WPF拖动总结 这篇博文总结下WPF中的拖动,文章内容主要包括: 1.拖动窗口 2.拖动控件 Using Visual Studio 2.1thumb控件 2.2Drag.Drop(不连续,没有中 ...
随机推荐
- The 15th UESTC Programming Contest Preliminary H - Hesty Str1ng cdoj1551
地址:http://acm.uestc.edu.cn/#/problem/show/1551 题目: Hesty Str1ng Time Limit: 3000/1000MS (Java/Others ...
- ipython安装(python3.6.1)
之前一直是在Python2.7环境下装的ipython,今天想在Python3环境下安装,主要是为了方便测试,按tab键可以补全,显示导入模块的方法. 1.首先安装Python3.6.1 wget h ...
- Linux ./configure --prefix 命令是什么意思?
源码的安装一般由3个步骤组成:配置(configure).编译(make).安装(makeinstall). Configure是一个可执行脚本,它有很多选项,在待安装的源码路径下使用命令./conf ...
- Ubuntu16.04安装印象笔记
Nixnote 是一个 Evernote 开源客户端,原名 Nevernote.Evernote 是一个著名的笔记等个人资料整理和同步软件, 因为 Evernote 没有 Linux 下的官方版本,因 ...
- 多线程中sleep和wait的区别,以及多线程的实现方式及原因,定时器--Timer
1. Java中sleep和wait的区别 ① 这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类. sleep是Thread的静态类方法,谁调用的谁去睡觉,即 ...
- java反射 - getXXX 与 getDeclaredXXX
1.getXXX 和 getDeclaredXXX java 里 Class<?> 有下面这些方法: 类似的方法有: 2.getMethod(s) 和 getDeclaredMethod( ...
- GRUB2 分析 (二)
接上一篇 实际上在512字节的MBR中,真正可用的空间并不多.除了一开始的跳转指令外,起始部分是一个被称为BPB的区域,即BIOS参数块(BISO Parameter Block).主要是FAT和NT ...
- 如何禁止ping
PING命令是个危险的命令,用它可以知道你的操作系统,IP等,为了安全禁PING是个很好的方法,也是防DDOS攻击的.应该是有外部网络试图连接你的UDP的1434端口,不知道你打了补丁没有. 黑客入侵 ...
- zookeeper可视化管理工具node-zk-browser安装
一.安装nodejs 1. 下载 wget https://github.com/joyent/node/archive/v0.10.35.tar.gz 2. 解压 3. 安装依赖 yum -y in ...
- Kubernetes 删除 namespace
一. 正常情况情况下的删除: kubectl delete namespace jenkins 二. 如果上面的方法不能删除,且namespace的状态一直显示为Terminating的话 要查看一下 ...