您当前的位置:delphi教程 > Delphi笔记 >  九天Delphi MIDAS架构

九天Delphi MIDAS架构

(未经允许,请勿转载或引用       在此不是因为所谓的"藏珍",而是因为如果未对整个架构有全面的认识,反而会使开发人员束手束脚,不如不用。而我的文字也并不能完整地将整个细节一一表达清楚)

题外话:以下所介始的设计并非是所谓”正统”的,与一些书本上的介绍或所流行(传)的常规做法存在较大差异,因此它很可能并不完全适合你,这个架构的产生,主要是我比较”懒”,其次也是最重要的,开发这套架的目的是因为系统对数据的处理要求变更的太历害,我需要这样一套能自由扩展的平台,而又不用写太多的代码,也不想将系统弄的太复杂。借用别人的一个名词:”草莽程序员”,我想这倒是很适合我,我的学习都是抱着学以致用、以用养学的目的,学习时也并不限定于Delphi,东看一些,西看一些,然后闲时闭目养养神,反思一下能否将这些技术或想法应用的更好,因为没有通篇研读过任何一位名家之作,像李大师的作品我是一本也没看过,实在很难将其提到一个理论的高度,如果一定要的话,像网络技术中的代理服务器、ARP欺骗技术,倒是和我以下要说的架构的设计原理有许多相似之处。
又及:感谢WNX公司的熊小姐,使我有幸踏入了程序员这个行列,也才有了下面的文字,在此再次表示感谢!
                              ---------BES96261(小李)

九天Delphi MIDAS架构的设想与产生
我想创建一个数据库管理系统的架构平台。要求:
  1>业务逻辑可以很方便的更改和控制;
  2>我比较懒而且日常工作较多,因而没有太多的时间,故在开发时不想写太多的代码,要求此架构完成后开发效率能有较大幅度的提高; [基类的架构能帮我完成大部分工作]
  3>我的数据在控制方面可能比较细,如用户A在某些功能窗体中仅可以看到自身创建或本部门的数据;[数据获取方式自由控制与扩展]
  4>在同一功能中,不同用户获取及提交的数据内容可不同(即不同用户可调用不同的提交业务逻辑); [提交方式自由控制]
  5>我希望我的每一个模块结构和业务逻辑都非常清晰,而且相互独立,但同时我又不想写太多重复的代码; [低耦合、高聚合]
  6>我希望在显示用户可编辑数据之前时,能对数据进行次加工后再显示,如将逻辑型显示为’V’或’X’,但发生修改时需保证数据的存储正确;  [数据在显示前或提交前可作再次加工处理]
  7>我希望在具体开发时界面不能太拥挤,如数据模块不要挤满了TDataSet或TProviderDataSet,最好是越精简越好,但系统设计要简单清晰;
  8>我希望我的用户界面在设计时能尽可能地少做或不做工作,但在有特殊需求时要能满足我的任何扩展
  9>我的数据库很庞大,每月的数据量增长一般为1.5~2.5G左右,而且随着系统的深入推行,增长速度会越来越快,要求系统不能因数据量的增加而产生大幅降低性能
  10>系统用户数一般为100~200个左右,而且分布较广,要求系统对网络的稳定性不是太高(能处理网络断线等异常),而且能够实现批量提交功能
  11>在获取数据时能使用存储过程、简单SELECT语句,自定义函数等任何方式获取供维护的数据,在提交数据时能使用任何自定义的方式去控制提交过程,也即意味着系统中的数据请求与提交功能能实现无限制地扩充。

  在基于以上要求的基础上,这套架构设想于2003年夏,因为工作时间安排与一些技术原因,断断续续的,在2003年底才得于完成,其后经历了两个中型项目(制造业MRPII)和三次小规模的改进后逐渐可算是比较完善了。
当然这套架构至现在还存在一些缺点:
  1>业务逻辑与数据库完全地结合在一起,如果在系统开发完成后需要转数据库系统的话,则需要将所有的存储过程重新修改或编写一次,其工作量将是庞大的
  2>对数据库SQL脚本的编写能力较高
  3>在前台客户端的编码要求倒是不高,但开发人员必须有面向对象思想的具体运用能力,否则难于应用架构所提供的各项服务

系统数据处理的总体思想:
    从分布上来说可分为三部分:客户端、中间层、数据库。
    系统响应请求流程依次为:1>客户端向中间层递交请求;2>中间层根据请求从配置文件中读取参数组装命令后传给数据库;3>数据库执行命令并返回结果给中间层;4>中间层将请求结果(允许在中间层对数据进行再次加工)返回给客户端。中间层在这里的作用为:请求—调用—返回。整个系统数据的传递都是通过接口的打包与分析包来完成的。
    我们知道,数据库调用一般可区分为:SELECT查询请求、存储过程的执行(在这又分为:返回结果集和无返回结果集两种)
    根据我们客户端的请求类型,一般可划分为:执行SELECT获取一个数据集(在这又分为:功能窗体的数据维护、下拉列表的填充数据<纵向>、根据一个关键字从表中查询相关信息<横向>)、提交编辑的数据内容、执行一个查询或操作功能(有返回数据集或无返回数据集)。
为此我们可将中间层的接口划分为以下七个:
  LoginAuth:登录用户的权限判断,如果正确,初始化中间层,使中间层可用;
  声明:
  //UserID:加密后的用户名
  //sPsd:加密后的密码
  //RstInt: 返回结果   以标识是否登录成功或错误类型
  procedure LoginAuth(const UserID, sPsd: WideString; out RstInt: Shortint);
  GetData:获取功能窗体的维护数据
  声明:
  //UserID:   用户编码
  //dstNm:    需求数据集名称
  //ParamStr: 条件值的集合,使用 '@' 作分隔符
  //sExpr:    前台传来的附带查询的SQL条件语句(用户根据数据集自己组合的查询条件)
  //Result:返回的数据集包  (值为TclientDataSet.Data)
  function GetData(const UserID, dstNm: WideString; ParamStr: OleVariant; const sExpr: WideString): OleVariant;
  GetData2:获取下拉列表填充数据
   声明:
   //UserID:   用户编码
   //dstNm:    需求数据集名称
   //ExprStr:    前台传来的附带查询的SQL条件语句
   //Result:返回的数据集包  (里面的内容为String类型数组)
  function GetData2(const UserID, DstNm, ExprStr: WideString): OleVariant;
  GetColStrs:将数据记录字段内容横向依次加入列表中
   声明:
   //UserID:   用户编码
   //dstNm:    需求数据集名称
   //ExprStr:    前台传来的附带查询的SQL条件语句
   //RstStrs: 返回的数据结果包  (里面的内容为String类型数组)
   procedure GetColStrs(const UserID, DstNm, ExprStr: WideString; out RstStrs: OleVariant);
  ExecProc: 执行存储过程,但不返回数据集,仅返回执行结果(成功/失效)
   声明:
   //UserID:   用户编码
   //dstNm:    需求数据集名称
   // ParamStr: 条件值的集合   (里面的内容为String类型数组)
   //Result: 返回的存储过程是否执行成功
   function ExecProc(const UserID, dstNm: WideString; ParamStr: OleVariant): Shortint;
  GetspData: 执行存储过程,返回数据集
   声明:
   //UserID:   用户编码
   //dstNm:    需求数据集名称
   // ParamStr: 条件值的集合   里面的内容为String类型数组
   //Result: 返回的存储过程执行得到的数据集  (值为TclientDataSet.Data)
   function GetspData(const UserID, dstNm: WideString; ParamStr: OleVariant): OleVariant;
  SetData:提交客户端经编辑过的数据,并返回执行结果
   //UserID:   用户编码
   //dstNm:    提交的功能数据集名称
   //ParamStr:  更新参数  (保留,暂未使用)
  //vData:  需更新的数据集  (值为TclientDataSet.Delta)
  //Result:    更新过程中需返回的列表 (更新过程中出现的提示或错误信息,未能更新的日志)
   function SetData(const UserID, dstNm, ParamStr: WideString; vData: OleVariant): OleVariant;
客户端亦建立与此一一对应的接口,在这里最主要的dstNm[功能数据集名称]可谓是系统的整个灵魂,没有此类配置文件的支撑,系统将无法进行任何作业操作。

客户端设计:
    既然是懒人,当然是希望越简单越好,工作量最好能降为0 ^_^,但功能却是必须满足需求,而且扩展与可维护性是越强大越好,稳定性在任何系统中都是必须的了,相信这也是众多开发人员所追求的,当然这也只能仅仅是追求。做系统时有太多的东西要相互权衡、取舍,这也应算是一件令人痛苦的事吧,每个开发人员都希望自己写的代码或设计的系统是完美无缺的,但事实却很难,尤其是在自己明知系统中还存在某些缺点或不足之处,却不得而为之或找不到更好的解决方法的时候,更是令人沮丧。好了,以下所描述的前台架构虽然还不是至善美,但应该说是较完善的,也算是我从业5年来所代表我现时水平的颠峰之作吧

TFrmBase:为系统中所有窗体的父项。负责窗体资源的释放。
TFrmBaseData:从TFrmBase窗体继承,为系统中所有单数据源数据(非主-从关系)维护窗体的父项。负责数据的初始化、存取、用户操作功能(增、删、保存、查询、打印、导出等)的实现及权限管理。
TFrmBaseMultiData:从TFrmBaseData窗体继承,为系统中所有多数据源(如主-从)数据维护窗体的父项。负责切换当前操作的数据源属性。
TFrmBaseSelect:从TFrmBase窗体继承,为系统中所有模式窗体的父项。负责显示的外观,公用功能的调用等。
TFrmBaseQuery:从TFrmBase窗体继承,为系统中所有查询窗体的父项。提供初始化查询参数,调用接口完成查询功能及数据的显示与打印功能。可重载显示控制与获取数据选择值过程。系统中将数据查询功能与及维护数据时的选择功能完整的合二为一,支持多选。
注:
  除登录窗体外所有窗体必须由TFrmBase中继承
  所有单表数据维护类窗体必须由TFrmBaseData中继承
  所有超过一个以上的数据维护类窗体必须由TFrmBaseMutiData中继承
  所有设置模式类窗体必须由TFrmBaseSelect类继承
  所有查询类与数据选择类窗体必须由TFrmBaseQuery类继承

TFrame_Item: 根据配置值调用任何查询类窗体进行数据选择,增强根据料品编码获取其它相关信息功能
TFrame_Button: 根据配置值调用任何查询类窗体进行数据选择
TFrame_List: 根据配置值产生下拉列表数据
注:
  所有料品的选择功能,必须使用TFrame_Item
  所有调用其它查询类窗体的选择功能,必须使用TFrame_Button
  所有下拉类(ComboBox)选择值,除常量值外,必须使用TFrame_List

---------------
待续....


查看回复
关于我们 | 网站地图 | 广告刊登 | 友情链接
Copyright ©2008 - 2010  Delphi2007.net  ,All Rights Reserved  粤ICP备09038647号
业务联系:allceoad#gmail.com QQ:20008835 最佳分辨率 1024×768