Delphi制作客户端系统功能组件分离的架构设计

Delphi制作客户端系统功能组件分离的架构设计

 

一.             目标:

a)         减少客户端exe程序的长度

b)        更好的组织源代码项目文件夹的结构

c)        使用基于接口的方式编程,减少模块之间的耦合度

d)        物理分离不同的功能组件

二.             实现方式:

a)         三类子项目:

l         主窗体和主框架项目

l         全局资源包项目(package BPL项目)

l         功能组件项目(com dll组件)

b)        主项目是普通的窗体项目或者MDI项目

l         主项目是整个项目的主框架,负责组织所有的功能组件

l         负责验证用户身份和初始化全局资源(如全局网络连接)

l         利用菜单或者子窗体调用功能组件

l         将“全局资源包”项目中的全局数据模块和全局变量单元增加到本项目中

c)        全局资源包bpl项目。

l         本项目封装全局使用的数据资源。

1.         本项目生成和安装后,其他项目将本项目产生的bpl包编译到里面

2.         主项目和功能组件项目都将本项目的包编译进去后,运行时本项目的所有变量均有一个唯一实例

3.         利用这一点可以共享全局数据以及不同项目之间进行数据交流

l         包含全局使用的数据模块

l         也可以放置全局变量。

d)        功能组件子项目是activeX Library项目

新建activeXLibrary项目,增加一个automachine object控件

l         令新接口继承IPlug接口

1.         IPlug是插件的通用接口,所有功能组件都要支持这个接口

2.         本接口支持getname函数和run函数

3.         getname函数用于返回插件名字

4.         run函数用来运行插件(如果插件是个独立的子系统的话)

l         子功能组件在新接口中增加函数,对外提供子功能

l         将“全局资源包”项目中的全局数据模块和全局变量单元增加到本项目中

 

三.             缺点和需要注意的问题:

本方案只是利用部分OO思想主要解决在客户端桌面程序上的物理分离问题,并不是纯粹用OO的方法做系统分析和设计。

特别是各个功能模块(COM组件)访问全局的数据模块,是通过delphi的共享包(bpl)来解决的。利用这种方式,com组件可以直接在编程中访问数据模块单元。(如果将数据模块作为单独的一个dll或者com,复杂度太大而且面临无法随时修改的问题。所以采用了这种方式。)

但是这样一来不同的功能组件都依赖于这个共享包,增加了功能组件耦合度。所以最好在每个功能组件项目中增加自己的数据模块,存放本模块专用的数据库资源。在全局数据模块中只保存全局链接、通用的查询组件(例如登陆和权限)等。尽量减少功能组件对全局数据模块的依赖。

可以在全局数据包项目中增加一个单元,将通用的业务逻辑操作封装在内,供所有项目调用;

可以在每个功能组件项目中增加业务逻辑操作单元,以便减少业务逻辑功能代码对界面的依赖性;

 

客户端和com组件的参数传递:

com只能使用标准的数据类型,对于编程中其他类型只能通过间接的类型转换来实现

1)对于某些控件对象(Tform类型和TwinContrl类型等)delphi数据类型,可以使用int类型与指针类型进行类型转换来实现,(必须在同一个进程内,否则指针无法这个能取映射),例如下面的实现:

接口方法:

  ITLogin = interface(IPlug)

    ['{06B82963-EE63-49B6-8989-8D3021928447}']

    procedure ShowSub(POwner: SYSUINT; ParentHand: SYSUINT); safecall;

    // POwnercom接口中参数类型是整形,在实现中当作指针来用

    // ParentHandcom接口中参数类型是整形,在实现中当作HWND类型来用

Com的接口实现:

procedure TTLogin.ShowSub(POwner: SYSUINT; ParentHand: SYSUINT);

type

  PComp=^TComponent;

begin

  if SubForm=nil then

    SubForm:= TSubForm.Create(PComp(POwner)^);

//利用强制类型转换,先还原成^Tcomponent的指针类型,然后解除指针参照

  subform.ParentWindow:=ParentHand;//直接赋值,隐式类型转换

  subform.Show;

end;

客户端

var

loginCom:ITLogin;

begin

    if loginCom=nil then

       loginCom:=CreateComObject(CLASS_TLogin) as ITLogin;

    LoginCom.ShowSub(cardinal(@self),self.Panel1.Handle);

// cardinal(@self)   先取地址,再显式类型转换

// self.Panel1.Handle 隐式类型转换

End;

 

 

 

 

2)对于不同进程空间内的转换,就不能简单的像上面一样利用指针进行数据传递了。对于“记录”类型:对于下面一种方法,应该小心使用:

记录类型的类型转换,利用MoveSizeOf

装入Variant:

  MyQuery: Record;

  V:Variant;

  P:Pointer;

 

  V:=VarArrayCreate([0,Sizeof(Record)],varByte);

  P:=VarArrayLock(V);

  Move(MyQuery,P^,Sizeof(Record));

  VarArrayUnLock(V);

 

取出 V:variant

var

P:Pointer;

MyQuery: Record;

begin

  P:=VarArrayLock(V);

  Move(P^,MyQuery,Sizeof(Record));

  VarArrayUnLock(V);

 

 

 

四.             项目文件夹结构

项目文件夹

Bin

Tmp

Frame

class

Interface

CommDataPk

Com组件1

Com组件2

编译后的可执行文件以及dll文件

编译产生的dcu等中间文件

主程序框架

项目所需要的公共工具类

项目所需要的公共接口单元(例如IPlug

公共数据模块和公共数据 bpl项目

功能组件  子系统

注意以下设置要点:

      

 

2

五.             日后的改进和演化:

增加主框架对子功能组件的组织能力(例如快捷键、主菜单、com组件的载入器);

增加服务器的功能,配合子功能做一些编码,将单纯的数据访问演化为逻辑中间件;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值