前言

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的前世今生的更多相关文章

  1. WPF 基础到企业应用系列2——WPF前世今生

    1.开篇前言       非常多时候了解一项新技术的历史和趋势往往比这项技术的本身价值还要重要.WPF作为一项新技术(已经三年多了.或者应该叫老技术了).我们都有必要了解它的来龙去脉,尤其是公司的CT ...

  2. 重复造轮子系列——基于FastReport设计打印模板实现桌面端WPF套打和商超POS高度自适应小票打印

    重复造轮子系列——基于FastReport设计打印模板实现桌面端WPF套打和商超POS高度自适应小票打印 一.引言 桌面端系统经常需要对接各种硬件设备,比如扫描器.读卡器.打印机等. 这里介绍下桌面端 ...

  3. 第五篇:在SOUI中使用XML布局属性指引(pos, offset, pos2type)

    窗口布局的概念 每一个UI都是由大量的界面元素构成的,在Windows编程,这些界面元素的最小单位通常称之为控件. 布局就是这些控件在主界面上的大小及相对位置. 传统的布局一般使用一个4个绝对坐标来定 ...

  4. WPF基础到企业应用系列6——布局全接触

    本文转自:http://knightswarrior.blog.51cto.com/1792698/365351 一. 摘要 首先很高兴这个系列能得到大家的关注和支持,这段时间一直在研究Windows ...

  5. WPF拖放功能实现zz

    写在前面:本文为即兴而作,因此难免有疏漏和词不达意的地方.在这里,非常期望您提供评论,分享您的想法和建议. 这是一篇介绍如何在WPF中实现拖放功能的短文. 首先要读者清楚的一件事情是:拖放主要分为拖放 ...

  6. 在uwp仿制WPF的Window

    移植WPF软件到uwp时碰到用作对话框的Window有多种处理选择.我个人认为最省事的是用ContentDialog模拟Window. 比如你想把上面这个WPF窗体弄到uwp里面去 1.修改Conte ...

  7. 【转】【WPF】wpf 图片指针处理

    我一直用GDI+做Winform 的基于指针的图片处理,这次下决心全部移到wpf上(主要是显示布局很方便)采用的图片是2512*3307 的大图 830万像素类库基于WritableBitmapEx ...

  8. WPF 基础到企业应用系列索引

    转自:http://www.cnblogs.com/zenghongliang/archive/2010/07/09/1774141.html WPF 基础到企业应用系列索引 WPF 基础到企业应用系 ...

  9. WPF拖动总结[转载]

    WPF拖动总结   这篇博文总结下WPF中的拖动,文章内容主要包括: 1.拖动窗口 2.拖动控件 Using Visual Studio 2.1thumb控件 2.2Drag.Drop(不连续,没有中 ...

随机推荐

  1. 20. Valid Parentheses(括号匹配,用桟)

    Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the inpu ...

  2. Java并发编程之CountDownLatch的用法

    一.含义 CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能.CountDownLatch是一个同步的辅助类,它可以允许一个或多个线程等待, ...

  3. Spring MVC 复习笔记03

    1. @RequestMapping 1). url映射 定义controller方法对应的url,进行处理器映射使用. 2). 窄化请求映射  3). 限制http请求方法 出于安全性考虑,对htt ...

  4. PHP jsonencode 已经json中包含 汉字的处理

    <?php $arr = array ( 'Name'=>'希亚', ); $jsonencode = json_encode($arr); echo $jsonencode; ?> ...

  5. PHP之PDO_MYSQL扩展安装步骤

    转载地址:http://www.cnblogs.com/qq78292959/p/4084868.html 看到CakePHP文档要求安装pdo_mysql扩展,于是就尝试安装了一下. 这里我的系统是 ...

  6. OpenStack之Nova模块

    Nova简介 nova和swift是openstack最早的两个组件,nova分为控制节点和计算节点,计算节点通过nova computer进行虚拟机创建,通过libvirt调用kvm创建虚拟机,no ...

  7. GoEasyWeb实时推送

    GoEasyWeb实时推送,轻松实现实时消息推送. Web页面订阅(约5行代码),服务器端推送(2行代码)就可以轻松实现,而且在高并发时消息推送稳定. 自己完全可以只花五分钟写出属于自己的第一个实时推 ...

  8. 深入理解Java枚举类型(enum)

    https://blog.csdn.net/javazejian/article/details/71333103 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(en ...

  9. Quartz(自动任务)中的触发器Trigger

    1.Quartz中的触发器TriggerJob 包含了要执行任务的逻辑,但是 Job 对何时该执行却一无所知.这个事情留给了 Trigger.Quartz Trigger 继承了抽象的 org.qua ...

  10. 事后调试.ZC资料

    1.查了一下,Delphi 程序 可以生成 map文件,可以用来 根据崩溃的内存报错 定位出错的代码位置 2.但是,Delphi程序 无法再崩溃的时候 生成dump文件 (这个不一定,研究了再说.记得 ...