zoukankan      html  css  js  c++  java
  • 架构反思案例之桌面程序架构案例

      这是一个大型的桌面版程序,前后有上百个人在这个软件的各个组件上奋战,这里介绍的部分基本上都是我留下足迹的部分,或者是参与设计,或者是重用这些组件,也或者是改造过这些组件。

    插件架构

      这个结构很清楚,是很多项目必选的架构,插件具有高度的灵活性和扩展性,这是这个架构突出的优点。

      采用了这个架构的程序,通常有两种做法:

      一种做法都是把每个单独的工程(单独的dll)作为单独的插件加载,在加载的过程中完成初始化的工作,在所有插件加载完毕以后根据插件的状态再初始化程序的界面,这也是这个项目的做法。

      另一种做法是动态加载,程序会监测特定的目录,当这个目录内有变化的时候,比如有可以使用的dll添加或删除的时候,动态的去加载或卸载该dll,然后修改相关菜单和界面,这种做法有点像硬件上所谓的“热插拔”的意思。

    动画设计

    这个部分的设计其实包含3个相关的内容:

    1. 渲染的方式

      这个设计的基础是Override对象,它记录的是数据的变化,当数据发生变化后,我们不直接修改原数据对象的成员,而是创建一个对象来保存这种变化。

      比如我们有一个对象叫长方形,它有一个成员数据表示其填充色,创建的时候我们直接设置为红色,然后当我们修改其颜色为绿色的时候,我们内部的实现是创建一个长方形对象对应的ColorOverride,该对象记录下了长方形的id和绿色。

      渲染的时候我们会先检查数据对象是否有相关的Override,如果有,则其优先级为最高,用于渲染,没有则以对象自身属性渲染。

    2. 动画元素的生成

      动画元素(Action对象)的生成就是基于Override的设计来实现的。

      我们使用简单的观察者模式,创建了ActionFactory对象来监听Override的创建,更新和删除事件,根据Override的变化我们可以捕捉相关的动画数据保存到Action中。

      这个思路工作很好,直到TransformAction的出现,因为当用户操作对象移动的时候,用户的输入与得到的Override不再是一一对应了。比如一个螺丝旋转360度与720度虽然从最终得到的矩阵来说是完全一样的,但是用户希望的结果却是不一样的,360度意味着转一圈,而720度意味着转两圈。而且转90度与转270度对于用户来说也是不一样的,虽然最终的位置是一样的。

      这种情况下,我们光靠监听Override已经不够了,此时需要完全记录下用户的输入数据,根据这个用户真实的数据,结合Override的值,我们就可以正确的还原用户的意图。

    3. 动画的播放

      播放动画过程是根据时间点,对生成的动画元素Action插值得到该时间点的属性值(EntityAttribute对象),然后根据这个属性值去更新相对应的Override,这样不断循环达到动画的效果。

      这3个部分结合起来就可以完成一个完整的动画制作和播放功能。

    命令模式

      这个系统的设计也是中规中矩的,这里主要包括以下几个要点:

    1. 程序启动的时候会初始化所有插件中的CommandDefinition。从这个类的名字上来说,这个类就是用于动态创建命令的。

    2. 命令以一个字符串作为唯一的标示符,执行命令的时候,只需要在CommandExecutor中指定这个标示符就可以了。使用字符串传递的方式来解耦效果还可以。 

    3. CommandExecutor拿到字符串以后会根据这个标示符找到相关的CommandDefinition对象,然后创建Command和CommandParameter对象,接下来就是执行Command。

    4. 执行Command的过程中并不是直接修改Model层中的数据,而是调用Model层中提供的Request对象修改Model中的数据,这样很容易维护双方接口的稳定性。

    Undo实现

      Undo/Redo是很多系统都会提供的功能,实现的方式也很多,就我经历过的系统,有两种相近的做法:

      第一种做法是Command对象提供undo/redo方法。当执行Command的时候,将其保存到一个堆栈中,执行Undo/Redo的时候调用相对于的Command的undo/redo方法即可。

      第二种做法就是这个项目中的做法,我们在Command执行的时候,创建了新的Transaction,执行完Command以后,Transaction也创建相关的Change,然后提交。提交的Transaction也是保存到一个集合中,Undo/Redo的时候就查找对应的Transaction然后undo/redo。

      实现中,Undo/Redo本身也是一个Command,但是是不记录在执行集合中的。

    事件模式

      这就是个典型的观察者模式,只不过有两个比较有意思的做法:

    1. 可以订阅一个类型的所有事件。以前的系统中,我们都是订阅一个对象的事件,这个系统的实现中,我们可以订阅一类对象的事件,这个比较有意思,事实证明也很有用处。实现这样的功能,与实现简单的观察者并没什么不同,只不过发事件的时候多绕个弯,让某些对象特别处理一下就可以了。

    2. 所有相关UI都监听Model的事件,然后更新。这是正常的操作,稍微有点区别的是UI的操作都提供了Preview的效果,这个时候修改的只是UI上的数据,最终提交的时候才会真正修改Model,然后Model发事件更新所有的UI。

    SaveLoad实现

      这个部分也没什么特别,就是访问器模式的应用,实现了将文件中需要持久化的树对象结构存储成二进制格式和xml格式。这个树对象接受一个访问器,这样提供了一个扩展点。 

  • 相关阅读:
    Python
    Django
    Markdown基础教程
    LR之Oracle 2tier协议录制Oracle脚本
    Python实现自动连接密码本破解wifi
    LoadRunner替换url字符串(可以同时替换多个)
    LoadRunner接口HTTP请求中中文乱码解决方案
    在WSL2中安装ArchLinux
    解决FastJson中"$ref重复引用"的问题方法
    java 后台通过IO流把文件传到前端并下载
  • 原文地址:https://www.cnblogs.com/dxy1982/p/3698971.html
Copyright © 2011-2022 走看看