系统架构设计
用最简单易懂的代码组织业务逻辑和实现系统功能。
在一个程序员的成长过程中,会出现写的代码先简单,后复杂,最后又简单的一个过程。在最开始写的简单的时候,是因为能力达不到,只能写一些简单的代码,考虑不到那么多的扩展。例如在弹出对话框的时候,直接按钮点击弹出对话框即可。当工作了一定的年限后,特别是3年左右,此时了解了一些设计模式和架构方法,我们做出的系统设计开始复杂起来,考虑的扩展和变化也越来越多,导致代码就越来越复杂。因为每个人对系统设计的理解深度和方式也不一样,自己设计出来的东西别人并不一定看得懂。所以一般接手3-5年程序员写的代码是很麻烦的。因为他可能用了自己用的比较熟悉的第三方库,或者自己熟悉甚至发明出来的一些设计方法,这都会让后来接手的人不易看懂。但如果一个程序员或设计师经验再服务一些,或者经历过这种事情的话,他在设计的时候就会为以后有人接手考虑。并且他也会考虑到哪些才是变化点,或者直接就不考虑变化点。例如以前一些在设计时要考虑对多个数据库的支持以及对UI变化的支持。对数据库的支持使用抽象工厂模式,对UI变化支持和为了封装业务逻辑而使用的MVC模式,至少在我们做的一般的应用项目中都是不必使用的。这样就能保证刚进入项目组的经验较缺乏的程序员也能很快的看懂代码。
例如我们在实现用户信息修改这个功能时,我们要得到要修改信息的用户编码或者用户对象,得到后传入用户编辑对话框,在对话框中读取该用户的最新信息并判断用户还是否存在,把读取到的用户信息赋值给各个信息输入框。当用户修改完这些信息后,系统要验证数据的合法性,如果不合法就弹出提示对话框。用户信息合法性验证代码要写在用户类中。最后更新到数据库中。
系统操作越简单越好,不要给用户太多选择。
一般情况下,用户经常用到的功能只是一个系统20%的功能。所以如何让用户在用这20%的功能时简单方便是系统设计和开发人员需要考虑的问题。
有以下几种方式:
-
把常用的功能按钮放在最显眼的位置,可以触手可及。
-
把又需要参数输入的界面设置好默认参数,不需要用户再一个个输入。
-
让用户想得即所得。当用户查看到一个界面时想要查看相关的信息,可能很方便的关联上。
-
最重要的事充分了解用户的业务,知道用户用系统需要做什么,最常用的功能有哪些。如果这个搞错了,那么很多工作就都白费了。
-
系统分层
系统分为领域模型层、数据访问层、系统逻辑层以及UI层。如下如所示:
Domains是系统的核心业务类,定义了业务模型,业务动作和一些流程。
DALs是访问数据库的类库,和数据库打交道的代码都会定义到此处。
BLLs我们称之为系统逻辑类,在系统中一些全局的管理信息类、系统状态存储和状态改变时的系统事件触发都定义到该类库中。
UIs是我们的系统界面类库。
系统分层不是固定死的,一般的业务系统分3-4层为宜,层次太多了,会增加系统的复杂程度。
-
创建程序集
对于一个CS系统,我们在创建程序集的时候会创建下面程序集。
Domains,定义核心业务的程序集
DALs,访问数据库的程序集
BLLs,定义系统全局状态控制相关类的程序集
UIs,定义UI的程序集
Commands,为主应用程序提供功能单元的程序集。
App,主应用程序
-
系统的核心业务如何定义
业务核心类直接用最简单的C#代码定义,一些比较明显的包含关系,需要在代码中定义出来。例如用户类属于一个角色,一个角色包含多个权限类。
那么在User类中就可以定义Role的一个属性,在Role中定义List<Limite>属性。如果为了获取一个用户是否有某个权限,在User类中可以定义 bool HasLimite(string pLimiteKey)函数。
-
如何访问数据库
访问数据库最好是使用一个第三方的对象持久化库,这样访问数据库会简单一些,代码量也少一些。Nhibnate,自己写的BM.DBMapping。
如果表是有一定的业务逻辑的,必须为每个表单独写一个DAL类,在该类中定义该表的增、删、查操作。但执行操作的Session需要从外面传进来,这样方便上层来组织事务逻辑。
-
如何控制数据库访问的事务问题
数据库事务如何控制,在什么地方控制数据库事务。当我们做一些添加操作时,有可能同时操作多张数据表,当所有的数据表都修改成功,任务才算执行成功。如果有一个出现异常,其他已经执行的操作都要回滚。
事务控制放在系统的哪一层?是放在DAL层还是放在DAL之上的层?
我的建议是放在DAL上面的层。DAL层只提供简单的针对该表的增、删、改或者相关表的关联查询,当系统执行某项操作,在写该操作的业务逻辑的地方,打开事务,然后调用多个DAL类对各个表进行操作,最后提交事务,如果遇到错误,则回滚。
-
DAL层要写一些什么代码
理论上每个DAL类都是对一个表进行操作的。该类应该包括对该表的增、删、改操作。另外一些查询,为了提高效率使用了关联查询,可能会涉及到其他的表。
每个DAL函数都要把自己所使用的连接传进来,因为很多系统的功能都是由多个DAL类在一起完成的。
-
如何组织UI
UI分为两种,一是和系统状态相关的、另一种是无关的。
和状态有关的需要把系统的Application类或者其包含的全局对象传进去。从全局对象中获取要显示的信息,并把操作的结果传回给系统的全局对象。
和状态无关的是一种相对比较通用的UI对象。这种UI在系统中功能比较单一、或者可能会被重复使用。使用这种类另一个好处可以减少耦合,把该类和系统关联的部分单独建一个类,这个类作用就是关联UI和全局信息。
我们使用WPF或Silverlight的第三方库都是使用的DEV,在使用的时候,我们尽量保持DEV控件的原始样式,这样当更改系统皮肤的时候,样式可以统一变换。
-
如何控制系统的状态
对整个系统状态的控制关系到系统代码的清晰程度。当系统启动的之后,该模块实际上就成了系统的运营中心。系统中存储的全局数据、配置信息以及状态信息都会存储在该模块中。一般来说该模块需要定义一个Application类作为该模块的根类。定义的其他状态控制器、全局信息等都挂接到Application类中,这些信息会跟着Application对象传递给每个需要的UI。
Application的作用,全局数据、配置信息、状态信息。
全局数据,例如我们要管理一些数据,这些数据的目录需要系统启动的时候就读到内存中,直到系统关闭。
配置信息,系统连接数据库的信息、FTP的链接信息等。
状态数据。这个是Application最重要的部分。当UI A执行了一个操作,需要通知给UI B做一个响应。这该如何实现?是要把UI B的实例作为参数传递给A吗?肯定不是的,这样会造成UIA和UIB的耦合。实际上UIA和UIB并不需要知道对方的存在,只要Application知道UIA和UIB都存在就可以了。
UIA执行操作后,会触发Application或者Application上挂接的一个对象的一个事件,当我们初始化UIB的时候,是需要把Application或者挂接的对象传进来的,并注册了这个事件,那么UIA操作后,UIB通过事件触发自然得到了通知,并改变自己的状态或执行其他的操作。
总结一下,也就是Application认识UIA和UIB,UIA和UIB也认识Application,但UIA和UIB是互不认识的。这就是低耦合。
-
如何布局主窗体
在主窗体上,布局主要是菜单栏,状态栏和主显示区。
菜单栏是有一个个命令按钮组成的,在最初的系统开发中,我们经常在菜单栏上加上按钮后,直接双击,VS会为程序员在后台代码中创建一个函数,然后我们就在该函数中写代码。但随着系统功能的增加,这么做会导致主窗体的代码越来越多(或者也可以简单的分离一下,但这解决不了我们面临的本质问题)。特别是当系统状态发生变化,菜单里面的按钮的可用状态等需要调整,那么实现这些的代码就成了程序员的噩梦。
怎么解决这个问题,那就为每个菜单按钮项写一个类吧。
当然我们会实现一个基类,在基类中就把Application传进去了,这样在每个菜单按钮的实现代码中,可以得到整个系统的状态和数据,什么时候自己需要改变状态,那就注册相应的事件吧。自己控制自己的状态时耦合最低的。各个菜单按钮项都不知道彼此的存在,大家都把状态统一交给Application管理,并响应Application的指挥,这就足够了。
那么主界面的后台代码所要做的工作,就是把这些按钮类加到菜单上就可以了。
-
GIS相关的数据如何存储
GIS数据我们认为是业务数据的一种形式,空间数据和其关联的数据通过SDE存储在数据库中。(参考ESRI为石油输油管线管理做的方案)
如果是CS系统,我们需要配置好Mxd文件,设置好每个图层显示的信息,包括渲染信息。当系统启动时,需要读取到系统配置的连接空间数据的信息,打开连接(Workspace),循环每个FeatureLayer或者RasterLayer,根据名称去Workspace中读取想对应的FeatureClass或RasterDataset。相当于重新设置一下数据源,主要解决因数据库连接改变而取不到数据的问题。第二也是为了安全,这样Mxd里面不用存储实际的空间数据连接参数,即使Mxd文件被别人拷贝走,也连不上真正的数据,保证了数据的安全性。
如果是BS(Silverlight)系统,也需要配置好Mxd文件,但此时的Mxd文件需要提前关联好对空间数据库的连接。然后通过ArcServer发布出来。BS系统在客户端通过ArcServer服务读取空间数据的信息。其实这和自己写WebServer读取类似。
在做需求时,尽量不要许诺用户自己去上传空间数据和发布地图服务,即使用户想自己上传和发布,我们要教给用户管理员如何使用ArcGIS桌面工具,不要自己开发工具。因为这个功能在日常使用频率是非常低的,但开发一个好用的却很费力气。
-
如何展示设备的当前数据
设备要显示的数据根据设备的不同,处理的过程也不一样。例如含水率设备,在系统中显示时就显示设备传来的最新的一条数据值。但雨量计可能是每0.5毫米降雨就回传一次,但在界面上显示的并不是0.5,需要是一定时间段的累加值。
所以我们要每种设备建一个实时数据表,需要记录设备的编号、值、以及更新时间。在服务器端运行一个服务,在采集或整理设备的数据,写到这些表里面。这样空间数据可以建立连接关联每个表即可。
-
附件如何存储
系统一般都会有附件,附件要以文件的形式存储到文件服务器上的磁盘上,文件按照上传的日期进行文件夹划分,这样可以避免一个文件夹下面出现太多的文件,也便于人工查找。文件上传时要给文件使用GUID重命名,避免一个文件夹下面出现重复名称的文件。
在数据库创建多媒体文件数据表,字段包括(唯一编码、名称、文件真实名称、上传时间、文件扩展名、文件大小、归属的记录编码)。
上传的文件夹在服务器上会在IIS里面发布出来,这样客户端就可以浏览了。
CS上传需要使用FTP上传的方式或者调用WebService。建议使用FTP吧。
我们做的系统一般BS系统服务器和文件服务器都是在一台机器上,但也有可能是两台。
如果是一台:
BS系统直接调用自己写的WCF写到本地即可。
如果是两台机器:
就需要在BS系统的WCF上把文件上传到FTP中。