两层还是三层?

    技术2022-05-11  139

    下面的这些东西是我转贴的。至于搞三层,我曾经用ASTA实现过,但是我一般用的是两层。也许对于小系统来说就比较好,但大系统的话,我看还是够怆。

    我应该研究这方面的东西如JAVA的一些应用服务器,以及DEPHI如何和它们通信;或。NET的一些解决办法。毕竟企业级应用才是王道。

    紧跟时代潮流才能前进。

    ——————————————————————————————————————————

    下面的东西都是转贴的,包括那个声明,都跟俺没关系。google搜出来的。在这之前俺的确有点觉得DataSetProvider+ClientDataSet和俺理解中的三层有点差别。看完这些讨论,俺的结论是:DataSetProvider+ClientDataSet的确是三层。但是对于初哥,很容易把它写成假三层,也就是把ClientDataSet关联到具体的数据表结构。这样做明显不符合界面与数据分离的原则。俺理解的三层:界面层通过对象的属性、方法与业务层通讯,而业务层则在对象的方法里面访问数据库。基本上每个对象对应一个表或几个相关的表。这种实现方法里面的一个重要问题是,如何把数据集封装成对象的属性。而ClientDataSet正好解决了这个问题。比起自己定义数据格式,自己定义数组,ClientDataSet要完整成熟得多。也就是ClientDataSet对应的是业务层处理过的数据集,并非原始的数据表。当数据库改动时,不需要对ClientDataSet做任何改动,只需要修改业务层的处理逻辑。这才是DataSetProvider+ClientDataSet的正确用法。如果仅仅把ClientDataSet对应到数据表,那这种三层是没有意义的,只不过是把原来数据库做的工作全部移到中间层了。

    ——————————————————————————————————————————原文地址 http://lincosoft.go.nease.net/originalarticle/twotierortritier.htm

      首先我声明一下,我是一个三层的忠实拥护者,无论是在理论层次上还是在实践层次上,三层结构都有不可代替的优势,况且现在流行三层,流行三层我们就只能学习三层,只有学好三层才能多赚Money养活妻儿老小(^_^)。我推荐这个帖子的目的不在于让大家思考到底是用二层好还是多层好,我是想让大家一块思考用DataSetProvider+ClientDataSet是不是真正的三层,这是我看这个帖子的主要收获。我的观点:DataSetProvider+ClientDataSet确实是三层,不过是一个经过高度封装的三层,是开发三层结构工程的最快速的技术(项目是有工期限制的,不能为了追求所谓真正的三层而拖延工期)。如果要是学习三层开发的话,最好还是看一看J2EE的东东,回头再把它的思想应用到Delphi的三层开发中来。好了,废话不多说了,大家看看下边的群儒论战吧!

    -------------------------杨中科

    win2000+sql2000开发中大型系统到底是用二层好还是多层好!

    我认为目前的技术来讲二层是优于多层的。目前的数据库技术已经比以前有了质的变化,ado的技术也已经到了2.8版了。我用delphi做过测试在p4平台,1.7Gcpu,40G硬盘,256兆内存下,在win和sql2000下可以在百分之一秒内完成下面几步:"连接上数据库,打开数据库,新增数据,查询若干行,关闭数据库,断开连接”。注:不是在百分之一秒完成一遍,而是近百遍。(使用ado操作sql2000),以上的例子说明完全可以在二层的开发环境中可以在要使用数据库的时候在连接数据库,在用数据库的时候就可以断开数据库,这种技术目前好象只能在微软的平台上实现。在连接数上来讲现在的多层也没有优势了。在可扩展性方面二层也要优于三层,两层可以使用模块化开发,一个功能一个模块,一个模块一部分人来负责,不同的模块组合成一个大的系统,增减功能只是增减模块。如是三层就要在应用应用服务器层添加新方法,又要在客户层设计界面,非要一个整体的模块拆成两部分,就象人首异处一样。在动态更新方面二层也要优于多层,如果一个正在工作中的应用服务器上的应用逻辑做了修改(增减的业务逻辑),一定要把应用服务器层给关掉,更换新版本后在重启应用服务器,在关掉的这段时间里所有的客户端都不能工作了(即使没有关联到增减业务逻辑的用户也要停止工作)。如果也增减了客户端界面的话还要来一遍二层技术的更改客户端界面。如果是二层的话就没用这个问题,因为所有业务应用都在客户端,我们可以使用微软的动态更新技术(如果有了新补定就在桌面的右下角就有个提示图标),我们做的只是发布新版本,更新动做由用户来决定。多层的就是因为有了应用服务器层其动态更新才不如二层方便。开发效率上讲就不用举例了,二层肯定是要优于三层的。二层易于调试、开发的程序易读这些都是优点。在分布式上现在的sql2000也是分布式数据库了,如果系统够大完全可以由多个独立的sql2000来构成一个大的逻辑上的sql2000。所以在分布式上多层也没有优势了。三层的技术是从unix上起源的,三层在unix上非常不错的,它也一直在unix上发展,在unix上连接一次大型数据库也得好几秒吧,如果没有应用服务器一直连着数据库,而采用直连的话,连接一次就得几秒,这样的产品就没法用了。先谈这么多,只想让大家讨论,我的意思就是现在一些书上讲三层怎么好,完全是在抄概念,可以说是张三李戴(Unix的三层帽子往win的头上戴!) 来自:SmallGhost, 时间:2004-3-1 10:29:36, ID:2477728我都是用二层开发系统的,多层太复杂了,我觉得不好!我做了一个连锁POS系统25个门店也没有用多层,数据包都是自己的格式,发到门店解包处理,这样更加安全! 来自:要离, 时间:2004-3-1 20:12:06, ID:2479163我曾经用二层开发过一个系统,有160(同时操作至少100)左右是客户端,效果还不错,但是在局域网内,所以我认为在局域网内如果用户数不超过200个是没很大的问题的,而且确实分模块开发比较方便,调式也方便,并且模块间的逻辑互不影响,如果联系到internet,三层还是有一定的优势吧 来自:zhbj, 时间:2004-3-1 20:33:13, ID:2479207是的,三层更适合广域网【注意b/s也是三层的特例】,比如跨国集团或者大的集团系统不适合用 b/s【集成业务逻辑确实不妥】,所以采用三层、多层但对于局域网、中小企业,无论从效力,开发周期、易于维护等方面都是最理想的 ,且借助 dbms的安全机制,原来的安全问题也有了质的进步 !特别是近期,越来越感觉 三层 好像要被 .Net 和 J2EE 所代替 来自:vb杀手, 时间:2004-3-1 20:53:31, ID:2479243感觉不能笼统的说究竟是三层还是两层的好。因为任何东西都没有通杀的局面。正如每天都有人在吵到底那种语言好一样。任何语言都有自己的优势,vc++有vc++的优势,delphi也有delphi的长处,其实vb也不错嘛。看是用来做什么项目了。同样道理到底三层好是是两层好同样是要看场合。并不是说三层就一定比两层好。要考虑很多方面的因素。两层实现起来比三层简单,简单就是一个很大程度上的优点,但是也不能说任何情况下都是两层比三层好。比如说如果一个系统的用户总是在1000人以上,那么用了两层的结构,实现起来就非常吃力了。三层的出现可以出现解决很多问题,用户多的话,维护应该是三层比较方便的。但是对于现在很多的项目来说,的确是应用两层的场合比较多一点。来自:mrzj, 时间:2004-3-2 17:48:04, ID:2480676to:vb杀手我认为如果1000人以上同时并发请求的大系统,用多层实现才是非常困难的(用winnt+sql2000实现的多层技术),而用二层(我前面提到的采取动态链接技术)实现起来才是非常方便。可以这么认为,并发数越多,二层的优势就越明显。例:现在的数据库完全包括原先多层用到的缓冲机制,比如打开表后,在关上表,这个表还是会在数据库的内存中存在一定时间的。我们软件的关表操作只是发送命令,具体什么时候物理关表(从数据库内存中把表清掉),这个由sql2000来决定。我在这里要澄清书上讲的误区,书上总讲并发数多了多层的执行效率会高过二层,这纯粹是在误导读者,也可以说作者在信口开河,胡说八道,在winnt和sql2000下多层实现在95%以上情况下的执行效率都超不过二层。也就是讲普通开发人员不管在什么情况下开发的二层产品的执行效率都要好过三层。这个论坛里已经有人做过测试了,用BDE,ado,采用二层与三层做的测试,我在这里看过,在这里肯定能找到的。 来自:KingPro, 时间:2004-3-12 16:23:46, ID:2499774传统的两层数据库系统,实质上是把数据存取和应用程序分离开来,数据服务器执行数据存取操作,客户机执行相应的应用程序。用户界面和企业逻辑层放在客户端,负责向服务器端请求和接收数据。当进行升级和维护时,任务变得特别繁重,要对每一个客户端的软件进行相应的升级和维护。多层数据库系统就可以很好的解决此问题:将中间层分离出来,由专门的服务器进行企业逻辑处理,如提供Web Services、Application Serve,使客户端成为“瘦”客户(只要浏览器即可)。从效率上说,两层系统效率高些。 来自:vb杀手, 时间:2004-3-17 11:10:55, ID:2506616to mrzj:兄台说的用两层实现起来比三层方便,这一点我我同意。毕竟三层的技术要求要比两层复杂。我们最终系统是让用户使用的,1000用户以上的系统不算小了,应该采用分布式结构,系统才能运行正常。如果采用两层,我觉得后台的数据库应付不过来。(就算1000个用户可以应付,10000个用户呢?)还有就是维护问题,倘若企业要修改业务逻辑,采用两层的结构,则必须对1000个client都进行更新,这是多么庞大的工作。如果采用三层,业务逻辑跟前台界面完全分离,只需要修改中间层就ok了。维护起来方便。 来自:Tassadar, 时间:2004-3-17 13:28:40, ID:2506975我再说几句,业务逻辑跟前台界面分离只是比较理想化的想法事实上,当需求或者设计发生改变的时候,大部分情况下无论业务逻辑还是界面都需要修改的。三层只是一个可供我们选择使用的一种技术而已,关键还是思想单纯从技术角度看,我认为只要用得好三层绝对是非常好的一种技术。 来自:vcanddelphi, 时间:2004-3-17 20:18:53, ID:2507916to mrzj:不要以为现在的计算机的工作能力提高了就可以解决速度问题。假设,在程序中,一个客户段上有TTable控件48个,其他的不算,这样,每一个TTable可吃掉你的服务器的40K的空间那将是一个客户端吃掉约1。5M的空间。好多用户一起使用的话,可是一个不小的开销的。用Tquery虽然不会吃掉这么多的空间,但好多的客户端一起工作也是一个不小的数字呢。并且,数据库不是永远那么点儿的,在现实中,它的增长是一个可怕的数字,这样在加上好多的客户,看你的数据库服务器能运行多快?如果是三层的话,可以用分布式来解决数据库服务器的压力,这样看是那一个快呢?还有一个问题,就是在两层上你不能进行智能判断,着又增加了服务器的工作量。举个例子来说:有一个公司的员工在11:00-12:00时都向一个饭店来定中午12:00的饭,你总不能每来一条就处理一条吧。我想二层的话,只能这样处理了。但三层的话,就可以在应用服务器中加以判断,比如要“米饭”的多少个人,等等,可以一次处理。属于个人意见,有不同之处,请指教。 来自:chnplzh, 时间:2004-3-17 20:55:48, ID:2507944我目前一直使用2层开发,3层有关心,不想过多评论。现在主要心思花在JAVA上面。TO KingPro:"当进行升级和维护时,任务变得特别繁重,要对每一个客户端的软件进行相应的升级和维护。"可以使用在线升级功能非常轻易地解决以上问题,我就已经实现了。TO vcanddelphi:" 一个客户段上有TTable控件48个",这其实不是主要问题,通过编程技巧均可解决。来自:mrzj, 时间:2004-3-18 15:08:43, ID:2509391vb杀手:你提到的客户端升级问题,现在的二层技术绝对要好过三层的,二层可以做到象微软公司的自动升级一样,大致的流程是,如果要对某一部分的客户端进行升级,开发者可以通过internet把要升级的文件发布到web服务器或ftp服务器,当客户端的用户一执行软件时,自动升级模块会自动的到网站上找新版本,如果有就问客户要不要升级。这样可以做到不用停止工作的升级,这在三层中是做不到的。TO:vcanddelphi,一个逻辑应用就对应一个Table控件的编程方法,不管是在三层还是在二可以说是不大好的设计方案。我们建立Table控件的个数,应该是在系统设计时看到底此系统有多少个并发逻辑应用,比如说有5个并发应用逻辑,那么我们就建5个Table控件。我为一个城市的连琐超市设计程序,也就不足十个并发应用,就是说我的datamodule里不到10个TDataSet。每个TDataset都在模块加载时才加载逻辑应用,当模块不用时释放以便下个应用模块使用。说到超市了,我就不信三层做的出来(只用二台服务器),一天全部分店的销售数据最少6千多万行,规模大些的分站一个满足条件的查询结果就有几百万行。如果二层一台服务器就可以做出来。我做的这个超市项目,总部有五百多人,二层应用,速度很快,网站上供货商查询销售与库存,一个月的数据,56K的modem二秒内就可以见到前三十个结果。并发量我做的测试是可以支持一秒150个并发。另一点就是省硬件的钱,服务器只用了一台HP6000做为总部应用,还有一台做数据备份,当然每个分店都有一台没这么好的了。 来自:捡垃圾去上网的人, 时间:2004-3-18 16:38:18, ID:2509555支持二层结构...为什么就不说了.....跨地域的使用就用b/s结构+离线版......效果比三层跨internet访问要好的多了......所谓的业务逻辑是骗人的.......三层要修改业务逻辑和两层的又有什么不同..怎么修改法都是要修改......不要吵这种问题了结贴吧,把所有的分合给我了....... 来自:vcanddelphi, 时间:2004-3-18 21:23:42, ID:2510111to mrzj and chnplzh:大家别忘了,如过客户段都是同一个程序那还好一点。如果客户段不是同一个程序但有一部分是相同的,但都是调用同一个数据中的东西,你的两层的方法,不是要写好多的重复代码吗,这不是有反“高质量代码编程”吗?并且,客户段要升级,那不是更麻烦? 来自:沙隆巴斯的主人, 时间:2004-3-19 11:27:53, ID:2510975看了上面的帖子,简直不能让我相信,大富翁里的主流意见居然是认为在大、中型开发里2层更有优势!!!!或许大家都看了李维的那几本书,觉得3层就在中间放了个DATASET与PROVIDER,前端用个CLIENT DATASET就是三层了。这样的伪三层大概是大富翁里用得最多的吧。这样的三层确实还不如两层的简单清晰。但真正的MT体系不是这样的。客户端没有任何的DATASET,没有任何的SQL语句,客户端根本不知道数据库是否存在。它只是通过GUI为用户提供一个与业务对象通讯的接口。应用服务器识别出不同客户端,可以确定允许它做什么和不允许它做什么(权限只有在服务器端实现才是可靠的,只靠在客户端通过变灰或隐藏菜单来保障权限那是不入门的初哥所为)。可以根据客户端的要求做各种事情(比如,开个户,客户端提供用户名称,电话等。。。,应用服务器去判断是否可以开户,开户成功就告诉客户端成功,否则通知失败,新开的用户对象可以缓存在对象池中,下次可以迅速调用,这比上数据库查效率高得多)。因为所有的资源、对象都由应用服务器控制,可以方便的进行对临界资源进行控制,而两层是难以做到的。MT在大型应用中的优势还多着呢,我实在不想赘述了。。。to 沙隆巴斯的主人:其实你说的也过于偏激了点,对于三层,我认为在小的程序上,用逻辑三层是一个比较好的想法,因为,开发起来快,又避免了两层的缺点。不要以为三层的东西都是来做大项目的,小项目用三层,想开发的快,又要对以后的扩展有好处。用一些数据库的控件来进行通信,不是一个很好的办法吗?难道非得用(com+ ,dcom等)通信才行吗?这样在小程序中,只会增加开发的工作量。 来自:沙隆巴斯的主人, 时间:2004-3-22 10:05:22, ID:2514642to vcanddelphi:你说的“逻辑三层”应该是个DO、BO、PO的设计模式吧,它们完全可以存在于同一个进程里面。这个模式应该来源于SMALLTALK的MVC(在UML中的分类非常接近于MVC,它认为对象有3类:实体对象、管理对象、边界对象)。应该说DO、BO、PO模型是MVC在数据库类应用中的一个具体化,而三层体系则是DO、BO、PO在分布式上的一个实现。至于是否非得用(com+ ,dcom还有什么COBRA、J2EE、SOAP)等来实现? 你完全可以自己来定义一套自己的RPC,我的同学在德塔斯曼就自己在SOCKET之上实现了自己的RPC。但这样做的效益如何就要另外考虑了。 来自:mrzj, 时间:2004-3-23 9:37:41, ID:2516722to 沙隆巴斯的主人你讲的中间层有线程池,缓冲池,比较有名的中间层的软件确是能很好提供这些。但我我觉得你上了这些中间层软件商的当了,这些中间层软件做的在好,也不如直接用数据库好,只要你的数据库不是为多个应用提供服务的,你讲的这些sql2000数据库全都有。在win2000下加sql2000,二层做的东西就肯定会比三层好,省时、省力、省钱、效率高!你讲的概念大都是在unix或java开发上用的,因为这些产品没有一个很好连接数据库的机制(根本就没有象ado这样高效连接数据库的方法),它们要靠中间层长连数据库(中间层一启动就霸占着数据库的连接),这才是主要的,缓冲池这样的概念才是次要的。就是因为有连接数据库性能不佳的原因后,才有了缓冲池什么的概念! 来自:chenglh, 时间:2004-3-23 14:39:41, ID:2517446也觉得Midas很麻烦。很多人没有把两层的程序写好,就开始追捧多层。个人认为现在比较好的两层结构(对于Delph5+ +Sql Server2000):1、多将业务操作写成存储过程。2、客户端将业务代码与界面代码分离。写在不同的单元里或者不同的类里。3、使用TClientDataset+DatasetProvider。利用其离线缓冲机制,嵌套表功能(其实觉得现在的ADO.NET很多都是抄袭TClientDataset)。4、如果进行大多数Mis系统进行oop分析,在这个结构里可以简化地这么处理:i)所有实体类都为TClientDataset,不继承,比如订单类type TOrder=TClientDataset。TClientDataset有足够的能力进行TOrder的属性读写,增删改功能,不用另定义新类(这后面有个TClientDataset+DatasetProvider+Ado一个体系支撑)。ii)事务处理类需要自定义,比如仓库管理类,这个类处理实体之间的合作,实体的流程。绝大多数的存储过程都是被事务处理类所调用的。 比如审核入库单。iii)字典类和辅助类。比如常用数据查找,还有常用的日志管理,用户管理等。(纯属私人经验)不过这些和即将到来的.net平台编程比起来可能落后了。来自:沙隆巴斯的主人, 时间:2004-3-23 14:24:19, ID:2517494to mrzj:各大DBMS确实都提供了缓冲池,减少了磁盘访问,从而提高了性能。 但你要注意的是DBMS提供的缓冲池是在哪个层次和以什么粒度提供的。DBMS能够提供的是记录还是对象?至于你说的非MS平台下数据库连接机制效率的低下我就只有笑一笑了,这个没什么好扯的。我从来没有认为多层既出,两层死光光。两层门槛低,有其生命力。 来自:chenglh, 时间:2004-3-23 16:29:14, ID:2517595用二层模式,当有1000个客户端同时连接服务器工作,那么在数据库服务器中就有1000个并发线程!------------------------------------用多层,1000个并发线程也不会消失。会出现在什么地方?中间服务器。这样中间服务器也会死机的(要么给客户端返回个服务器忙的信息。谁忙?我,中间服务器,不是数据库服务器,忙。或者忙得没空去返回信息。)。可以配置多个中间服务器。当然可以。但Sql Server也可以使用服务器群集技术,具有高度的可扩展性,对Sql Server不了解,要是Sql Server也能给要求排队,或者返回个服务器忙的信息的话,中间层真的不需要了。仔细想想,2层多层的中心主题还是扩展性和安全性。来自:yu_ting, 时间:2004-3-24 11:03:55, ID:2518913但大趋势是很明显的,但我对C/S结构你点怀疑。我去年做过一个农业税征收软件,用的是C/S结构,用DCOM加SocketConnection进行连接,服务器使用InterBASE数据库。死机现象非常明显。用的就是上述“李维”方式的三层。 来自:youngyxy, 时间:2004-3-24 11:43:36, ID:2519072我们的论题好像应该明确一下,‘Delphi编程,SQL2000作为后台DBMS情形下,程序结构问题,究竟多层有什么好过2层之处’。首先,界面逻辑与业务逻辑混在一团的传统2层,不便于系统维护与演进。所以应该把界面逻辑与业务逻辑清晰的分离,至于数据访问部分,2种处理,一种是象java做法,做一个数据访问层,业务层仅仅访问数据层,而不是直接访问DBMS。另外一种做法,就是省略数据访问层,业务层直接访问DBMS。以上2种做法各有千秋。个人觉得业务层直接访问DBMS在delphi中似乎更好。注意,在这里没有所谓midas出现。 来自:太阳河上, 时间:2004-3-24 11:46:31, ID:2519083本人的经验是,在中小企业里,两层意思,三(多)层编程,所在的查询和少量更改在服务层,而部分计算在客户机进行,理由是这样:1、更强扩展性。c/s和b/s混用,编程量减少。2、实践表明,大量的数据处理就在那几台机中,其它的就是查询或少量更新。3、现在的客户机能力非常强,充分使用了客户机的能力。4、分布的最终目的:利用所有的资源,一台服务器处理很多事肯定会慢,我们很少有大型服务器,由多台客户机进行计算,这也可能是比较好的方法。严格来说,选用哪种是根据项目来定,但我以为:要稳定,更快报地交出项目,合理的利用资源,更少的硬件投资,发挥更大的作用. 来自:vcanddelphi, 时间:2004-3-25 8:25:23, ID:2520530to mrzj:我还忘了一点要说的,就是“钱”的问题。现在的好多的数据库软件(如SQL SERVER)都是安着接点来收费的,每增加一个接点就要多大几百块呢。如果是5个接点的SQL SERVER和25个接点的SQL SERVER好象要差一万多块吧(具体的RMB不太清楚,要根据代理商那里了),这样,如果你做出来,一个两层的数据库服务软件,就如你说的,数据库可以完成数据的处理,可是这样要比多买一太一般的(P4应用服务器)也要贵出来好多的钱吧。如果你用一个应用服务器来管理的话,那就不必花那么多的钱给数据库的厂家了。再有,如果客户段,超过25个接点的话,你还用两层的话,那你只能用SQL SERVER 注册CPU版的了,大约好象是20万,好象这个费用,就高的太离谱了吧。-------也许你现在的客户段没有这么多,但有一天,真的那么多了,难道先让用户花20万来买一套SQL SERVER吗?别的钱还没有算呢?天啊,这样下来,可是个大工程了。(注意:在大一点的工程里可不要用D版的啊,这样我的一个朋友遇到过,还没有调试完,数据库服务器就被人给版走了!呵) 来自:vcanddelphi, 时间:2004-3-24 22:56:38, ID:2520575to 沙隆巴斯的主人:你的观点我大部分都同意,我看我在这放面和你还是有一定的距离的,希望你发言能多说一点,不要点到为止,我可要想好长时间,才能认同的。///说说你以前说的吧:///“或许大家都看了李维的那几本书,觉得3层就在中间放了个DATASET与PROVIDER,前端用个CLIENT DATASET就是三层了。这样的伪三层大概是大富翁里用得最多的吧。这样的三层确实还不如两层的简单清晰。但真正的MT体系不是这样的。客户端没有任何的DATASET,没有任何的SQL语句,客户端根本不知道数据库是否存在。它只是通过GUI为用户提供一个与业务对象通讯的接口。”///你的前一段说的有一定的道理,我看着好向是大多数开始开发三层数据结构的入门时用的,不过在实现快速,的开发三层的时候,这种伪三层的开发方式也有他的优点,比如:好设计,开发的快,还有就是可以解决我上面提到的“钱”的问提。 缺点当然是有的,最大的缺点应该是:移植性和可重复使用性了,吧。---个人观点,请指教。你的第二个观点我感觉,正好他的优缺点是和第一个是相反的。我现在开发了一个客户端完全没有数据库组件的程序,只不过是客户端与服务器端都在一台计算机上的小程序,不用什么网络通信,就可以连接上的那种。可是在我看来还是没有达到你提出的那种理论,好象还有一段差距。再有就是如果我把 client/appserver/dbserver 分别放在三台计算机上的话,我对与他们之间如何通信还没有很好的理解,请指教。 来自:沙隆巴斯的主人, 时间:2004-3-25 11:59:49, ID:2521346TO vcanddelphi:关于你的第二问,我假设你的应用需求如下:有一些用户位置较为集中,需要进行复杂操作,称为A类用户;一些用户很分散,进行较简单的操作,称为B类用户。你为A类用户提供订制的客户端程序(一般的DELPHI程序);为B类用户提供浏览器程序。假设你按照三层进行设计,那么你一般需要一个数据库,一个应用服务器程序(例如COM+的),一个WEB服务器。A类用户访问应用服务器;B类用户访问WEB服务器。应用服务器程序访问数据库。现在的问题是:WEB服务器访问数据库?还是访问应用服务器?两种方法都可以,但WEB服务器访问应用服务器是个更好的方法(当然,这样的系统要更复杂些)。WEB服务器这时候是应用服务器的一个客户端,从应用服务器的角度看出去,它与A类用户的客户端没有任何区别。这样,业务逻辑仅存在于应用服务器中,WEB服务器只需要关注表现形式的问题了。 来自:vcanddelphi, 时间:2004-3-26 21:17:52, ID:2524436TO 沙隆巴斯的主人你好向理会错我的意思了,但同样感谢你的回答,我的另外一方面的问题的到了解决。我是问。如果,在客户机子上不放任何数据库控件,的话,他与服务器的通信问题。正如你所说的,如果应用程序服务器用COM+,那客户端(你说的A类的那种)该怎么和它建立通信呢?1。如果客户端使用 DCOM或和他同等的控件 那可以和应用程序服务器可以建立通信, 可是提取数据是应该怎么设置呢?2。如果不用 DCOM和它同等级的控件,那通信应该怎么实现呢?谢谢!!!!不知,你有没有QQ号,我想和你多学习。 来自:hygsxy, 时间:2004-3-28 0:47:38, ID:2526074两层还是三层讨论这么多了,到底还是没争论出个名堂。我觉的选用2层,还是3层,那要看你的应用的目标。需求不一样,两层和三层当然有区别。打个比方。你要给那些客户机数量比较少,分布集中的中小公司开发POS,ERP,MIS系统,那自然首选两层的C/S结构,但如果面向的是些大公司,他们的客户机数量多,分布广,并且商业逻辑规则复杂,出于易维护,易扩展,易分发的目的,你当然要选三层了。即便有些时候三层的C/S结构的运行速度慢,但权衡了分发,维护,扩展,钱等的利弊后,我门有时后还是要选择三层。两层的数据库程序做的是比较多的,技术也相对比较成熟,但我们中国人,尤其是一些软件公司,和客户,都有追捧新技术的毛病,社会上流行什么技术,就要什么技术,管她优劣与否,其实两层本身就可以解决问题的,但为了满足他们的来自:mrzj, 时间:2004-3-29 9:42:04, ID:2527275数据量越大,用户数越多,二层越快,认为三层会快的观点是错的!慎用COM+或DCOM,不稳定的关键就在这里,用户数多、数据量大了的时候中间层会经常死机的!COM+和DCOM微软都淘汰了,改成.net了,它的点缺点太多了,各位还觉得它好?要是90年代中后期谈这个还成,但是经过这么多年的实践,com和dcom的方式太不稳定了,慎用啊!小型应用还可以,大型的绝对不要搞com的形式,除非你的中间层服务器可以每天都重启运行!数据量越大,用户数越多,二层就越快! to mrzj:如果我们做一个试验:比较使用两层技术开发的软件和使用三层技术开发的软件从数据库中取出相同数量的数据的速度。那么这个比较的结果是什么呢?一定是使用两层技术开发的软件的速度快。这个结论不论是从理论上还是实践上都是正确(增加的中间环节必然会产生负面影响)。但根据这点而推论出使用两层技术开发的软件比使用三层技术开发的软件在实际应用要快的结论,就缺乏依据了。因为在二层中,要访问数据就要访问数据库;而在三层中,设计合理的应用服务器可以提供缓冲池,可以大幅降低对数据库的访问。这时候二层因为结构简单而带来的访问数据库速度快的优势就被抵消了。这样,最终性能上谁优谁劣就与应用的特性,规模等一系列因素相关。但作为一般性的结论,随规模增大,三层在性能上具有优势。与mrzj兄的讨论局限在性能上(速度),其实这只是在做系统架构设计时需要考虑的一个方面,此外还有灵活性、可扩展性、健壮性、经济性等一系列的方面考虑。 当把这些因素全部考虑进去,那么随着应用规模的增大,三层体系的优势将更大。关于中间层会经常死机的问题,其症结出在程序设计不合理上。举个mrzj兄最爱提起的例子:SQL_Server2000数据库本身(这是MS的产品中比较经典的东西,MS为了因应反垄断压力,向部分国家开放了源代码,而我的老师在02年作为清华在数据库方面的代表访问了MS的数据库事业部,因此我也了解一些SQL2000内部的细节)。整个SQL2000的开发团队有500人,他们按照开发内容又分成次一级的团队,例如,某个组是负责语法分析器的开发的,另一个组是负责查询器的。那么语法分析器和查询器是如何结合的呢?他们之间的接口是如何定义的呢?各个组件的边界是由SQL2000的系统架构师划分的,其接口定义就是按照COM的格式进行的。也就是说,SQL2000本身就是一个由多个COM组件构成的整体。不知道mrzj兄是否也觉得它不稳定,经常死机。其实,MS的东西几乎全部都是按照COM规范提供的,比如OFFICE系列(所以我们可以在DELPHI创建它们的自动化对象)、IE(QQ的那个什么浏览器就是在IE外面加了个套)等等等等。学过VC的人都知道MFC这东西,它是MS推出的一套商业化产品,但据我了解到的情况,MS内部是根本不用MFC的(MFC是给外人用的业余架构产品),他们的程序是用C++按照COM规范写的(现在流传出来了部分的MS源码,大家可以看看是不是这样!)。没错,现在MS主推.Net,那么.Net与COM(COM+等)是什么关系?其根本性改进在哪里?也就是“自描述”!!!这是.Net最革命性的进步。(但还是晚了JAVA许多,JAVA一早就实现了自描述,这是JAVA对C++的最大的进步。自描述被认为是近10年在软件工业化上取得的最大的成就,它是组件技术的重要一环,相比之下OO技术都没它重要)因此,可以看出.Net并不是推翻了COM,而是更进了一步,适用与COM的依然适用于.Net。 来自:mrzj, 时间:2004-3-29 17:50:01, ID:2528452to 沙隆巴斯的主人你提到com开发,微软做出来的确是非常的稳定的,我指的是用开发工具delphi开发的com组件开发出的中间层产品来操作数据库是不稳定的。你提到中间层的线程池、缓冲池等,我认为数据的线程池和缓冲池比中间层提供的要好!你提到的“设计合理的应用服务器可以提供缓冲池”这点用delphi提供的mts等技术开发出的中间层产品是根本就做不到的!weblogic,webshare等中间层产品是有这些功能,但使用这些中间层产品,用delphi开发客户端的人更是少而又少。多层的技术,不大适合在微软平台上使用。多层是为unix平台用的!三层在微软平台上用,就如二层在unix平台上用一样! 来自:沙隆巴斯的主人, 时间:2004-3-29 22:29:16, ID:2528839to mrzj:》》我指的是用开发工具delphi开发的com组件开发出的中间层产品来操作数据库是不稳定的。》》对于这句话可以有两种理解:1、BORLAND的产品DELPHI对COM的支持不好,用它是无法开发出合格的操作数据库的软件产品;2、开发人员掌握不了用DELPHI开发基于COM的操作数据库的软件的技术。我想以上两种说法,BORLAND公司不会同意第一种;广大的DELPHI开发人员不会同意第二种。》》我认为数据的线程池和缓冲池比中间层提供的要好》》DBMS提供了缓冲技术支持,而且现在各厂商的产品都做得很好。但这种支持是在数据库层次上提供的,是细粒度的(一般来说是这样,大部分的DBMS是关系型的,因此其提供的缓冲也是关系元组——记录行这一级的;ORACLE8i既以上虽然支持对象型数据库,但由于对象型数据库的不成熟,可以不于考虑)。而在面向对象的软件中,我们操作的是对象。为此,各中间件厂商在其产品中都提供了各种容器,用来提供对象缓冲支持。从上面的论述,mrzj兄应该可以明白:即使有了DBMS的缓冲支持,如果在中间层继续使用缓冲技术,依然可以使系统性能取得巨大提升。而从实际效果来看,这种性能的提升的效果远大于由于增加了一层中间环节而带来的负面影响(当然,这还是与规模大小以及应用的特性有关)。》》“设计合理的应用服务器可以提供缓冲池”这点用delphi提供的mts等技术开发出的中间层产品是根本就做不到的!》》这就奇怪了,我自己手上的软件是怎么实现缓冲池的呢?有兴趣的可以参见:http://www.delphibbs.com/delphibbs/dispq.asp?lid=2463683 》》多层的技术,不大适合在微软平台上使用》》得出这个结论不知道你有什么依据?若是真的,MS的.Net项目就应该马上流产,不用再搞了;COM+也应该从NT5.x里面清除掉了。还是举SQL2000的例子,mrzj兄应该知道SQL2000是可以通过发布/订阅模式来进行分布式数据库集群的,那么,你知道它的发布/订阅是用的什么技术吗?就是COM+!!!! 来自:沙隆巴斯的主人, 时间:2004-3-30 14:28:03, ID:2529767to zhousi:1、datamodel还是removete datamodel都是无关紧要的,所有的datamodel都只是个容器(HELP里说得很清楚了)。2、我的设计是只有一个datamodel。3、datamodel里有个一个connection池和一个query池。关于这两个池可以见下面帖出的代码。(池的清理算法较简陋,希望同行给以改进建议)**************************connection池*****************************************unit UDataConnPool;interface usesSysUtils, Classes, DB, ADODB, Contnrs, Windows, ExtCtrls, UBase;typeTDataConnectionPool = class(TComponent) //数据库连接池类privatefConnParameter : RConnParameter;fConnList : TComponentList;fCleanTimer : TTimer;procedure fCleanOnTime(sender : TObject);function fMakeConnStr : String;function fCreateADOConn : TADOConnection;//创建新的空闲连接procedure fClean;//清理 (清理长时间不用的和长时间不归还的(死的)连接){ Private declarations }protectedfunction getConnCount: Integer;public{ Public declarations }property ConnCount: Integer read getConnCount;constructor Create(owner : TComponent; connParam : RConnParameter);overload;function getConn : TADOConnection;//取得空闲连接procedure returnConn(conn : TADOConnection);//归还连接end;implementationconstructor TDataConnectionPool.Create(owner : TComponent; connParam : RConnParameter);varindex: Integer;begininherited Create(owner);fConnParameter.ConnMin := connParam.ConnMin;fConnParameter.ConnMax := connParam.ConnMax;fConnParameter.RefreshTime := connParam.RefreshTime;fConnParameter.dbUser := connParam.dbUser;fConnParameter.dbPass := connParam.dbPass;fConnParameter.dbSource := connParam.dbSource;if fConnList = nil thenbeginfConnList := TComponentList.Create; //创建数据库连接列表tryfor index := 1 to fConnParameter.ConnMin do //创最小连接个数个建数据库连接beginfConnList.Add(fCreateADOConn);end;exceptend;end;if fCleanTimer = nil thenbeginfCleanTimer := TTimer.Create(Self);fCleanTimer.Name := 'MyCleanTimer1';fCleanTimer.Interval := fConnParameter.RefreshTime * 1000; //清理程序启动的时间间隔fCleanTimer.OnTimer := fCleanOnTime;fCleanTimer.Enabled := True;end;end;procedure TDataConnectionPool.fClean;variNow : Integer;iCount : Integer;index : Integer;beginiNow := GetTickCount;iCount := fConnList.Count;for index := iCount - 1 downto 0 dobeginif TADOConnection(fConnList[index]).Tag > 0 thenbeginif fConnList.Count > fConnParameter.ConnMin thenbeginif iNow - TADOConnection(fConnList[index]).Tag > 600000 then //超过10分钟不使用的、大于连接池最小数目的空闲连接将被释放beginfConnList.Delete(index);end;end;endelse if TADOConnection(fConnList[index]).Tag < 0 thenbeginif iNow + TADOConnection(fConnList[index]).Tag > 3600000 then //被连续使用超过1小时的连接(很可能是死连接将被)释放beginfConnList.Delete(index);if fConnList.Count < fConnParameter.ConnMin then //若小于连接池最小数目,则创建新的空闲连接beginfConnList.Add(fCreateADOConn);end;end;endend;end;procedure TDataConnectionPool.fCleanOnTime(sender: TObject);beginfClean;end;function TDataConnectionPool.fCreateADOConn: TADOConnection;beginResult := TADOConnection.Create(Self);Result.ConnectionString := fMakeConnStr;Result.LoginPrompt := False;Result.Open;Result.Tag := GetTickCount;end;function TDataConnectionPool.fMakeConnStr: String;beginResult := 'Provider=MSDAORA.1;Password=' + fConnParameter.dbPass +';User ID=' + fConnParameter.dbUser +';Data Source=' + fConnParameter.dbSource + ';Persist Security Info=True';end;function TDataConnectionPool.getConn: TADOConnection;varindex : Integer;beginResult := nil;for index := 0 to fConnList.Count - 1 dobeginif TADOConnection(fConnList[index]).Tag > 0 thenbeginResult := TADOConnection(fConnList[index]);Result.Tag := - GetTickCount; //使用开始计时 (负数表示正在使用)end;end;if (Result = nil) and (index < fConnParameter.ConnMax) then //无空闲连接,而连接池数目小于允许最大数目(fMax),创建新的连接begintryResult := fCreateADOConn;Result.Tag := - GetTickCount; //使用,开始计时 (负数表示正在使用)fConnList.Add(Result);exceptend;end;end;function TDataConnectionPool.getConnCount: Integer;beginResult := fConnList.Count;end;procedure TDataConnectionPool.returnConn(conn: TADOConnection);beginif fConnList.IndexOf(conn) > -1 thenbeginconn.Tag := GetTickCount;end;end;end.************************************query池************************************unit UAdoQueryPool;interface usesSysUtils, Classes, DB, ADODB, Contnrs, Windows, ExtCtrls, UBase;typeTAdoQueryPool = class(TComponent) //AdoQuery缓冲池类privatefAdoQueryMin : Integer;fAdoQueryMax : Integer;fAdoQueryList : TComponentList;fCleanTimer : TTimer;procedure fCleanOnTime(sender : TObject);//按时整理缓冲池function fCreateADOQuery : TADOQuery;//创建新的AdoQueryprocedure fClean;//整理 (清理长时间不用的和长时间不归还的AdoQuery){ Private declarations }protectedpublic{ Public declarations }constructor Create(owner : TComponent); override;function getAdoQuery : TADOQuery;//取得空闲连接procedure returnAdoQuery(qry : TADOQuery);//归还连接end;implementation{ TAdoQueryPool }constructor TAdoQueryPool.Create(owner: TComponent);varindex : Integer;aAdoQuery : TADOQuery;begininherited ;fAdoQueryMin := 10;fAdoQueryMax := 100;fAdoQueryList := TComponentList.Create(False);for index := 1 to fAdoQueryMin dobeginfAdoQueryList.Add(fCreateADOQuery);end;if fCleanTimer = nil thenbeginfCleanTimer := TTimer.Create(Self);fCleanTimer.Name := 'MyCleanTimer1';fCleanTimer.Interval := 600 * 1000; //清理程序启动的时间间隔(10分钟)fCleanTimer.OnTimer := fCleanOnTime;fCleanTimer.Enabled := True;end;end;procedure TAdoQueryPool.fClean;variNow : Integer; //当前时刻iCount : Integer; //List大小index : Integer;beginiNow := GetTickCount;iCount := fAdoQueryList.Count;for index := iCount - 1 downto 0 dobeginif TADOQuery(fAdoQueryList[index]).Tag > 0 then //若空闲beginif fAdoQueryList.Count > fAdoQueryMin then //若AdoQuery个数大于最小值beginTADOQuery(fAdoQueryList[index]).Free;end;endelse if TAdoQuery(fAdoQueryList[index]).Tag < 0 thenbeginif iNow + TADOQuery(fAdoQueryList[index]).Tag > 10800000 then //被连续使用超过3小时的AdoQuery(很可能是死的),释放beginTADOQuery(fAdoQueryList[index]).Free;if fAdoQueryList.Count < fAdoQueryMin then //若小于缓冲池最小数目,则创建新的空闲AdoQuerybeginfAdoQueryList.Add(fCreateADOQuery);end;end;endend;end;procedure TAdoQueryPool.fCleanOnTime(sender: TObject);beginfClean;end;function TAdoQueryPool.fCreateADOQuery: TADOQuery;beginResult := TADOQuery.Create(Self);Result.Tag := GetTickCount; //空闲,开始计时(正数表示空闲)end;function TAdoQueryPool.getAdoQuery: TADOQuery;varindex : Integer;beginResult := nil;for index := 0 to fAdoQueryList.Count - 1 dobeginif TADOQuery(fAdoQueryList[index]).Tag > 0 thenbeginResult := TADOQuery(fAdoQueryList[index]);Result.Tag := - GetTickCount; //使用开始计时 (负数表示正在使用)end;end;if (Result = nil) and (index < fAdoQueryMax) then //无空闲AdoQuery,而缓冲池数目小于允许最大数目(fAdoQueryMax),创建新的AdoquerybegintryResult := fCreateADOQuery;Result.Tag := - GetTickCount; //使用,开始计时 (负数表示正在使用)fAdoQueryList.Add(Result);exceptend;end;end;procedure TAdoQueryPool.returnAdoQuery(qry: TADOQuery);beginif fAdoQueryList.IndexOf(qry) > -1 thenbeginqry.Tag := GetTickCount; //开始空闲计时end;end;end.来自:vcanddelphi, 时间:2004-3-31 23:38:31, ID:2532544to 沙隆巴斯的主人:对于COM我不赶妄下评论。但DELPHI下的ADO编写数据库程序有些问题,ADO控件封装ADO后,有的功能老是出问题,不知是,DELPHI封装的不好,还是MS没有把ADO真正的核心功能给发布呢?就那TAdoQuery控件的DELETE功能来说,在D5下打补丁都不行,害的我还的用SQL语句来实现删除功能,具网友说,这个问题在,D6,D7下都有问题。对了,还有就是问一个幼稚的问题,COM+是不是建立在OLE之上的呢?我知道ADO是建立在OLE之上封装的。如果“COM+是不是建立在OLE之上”成立的话,那他们有没有什么关系? 来自:proman, 时间:2004-4-1 22:24:09, ID:2534602To: 沙隆巴斯的主人在多线程的情况下,你如何保证在你读Tag时,别的线程没有去修改它呢?结果是很有可能多个线程同时取得了一个连接.你的读取动作并没有放在同步保护中啊,我看着好象总是有问题的,也许测试中很难发现,但确实是一个问题吧. 来自:沙隆巴斯的主人, 时间:2004-4-2 0:29:36, ID:2534699to proman:你说的确实可能是个隐患。DELPHI没有提供对方法的同步设置能力,不知道有什么好办法可以让某个方法不可重入(如果在方法开头设置互斥量,依然有多线程同时访问此互斥量的隐忧),还请proman兄赐教高招。 来自:proman, 时间:2004-4-2 8:13:02, ID:2534775使用TCriticalSection就可以做到。下面是我的连接池的代码,我比较简单,还没有做计时释放的工作。constructor TConnectionPools.Create;beginFConnList := TList.Create;FCriticalSection := TCriticalSection.Create;FTimeout := 5000;FMaxCount := 15;FSemaphore := CreateSemaphore(nil, FMaxCount, FMaxCount, nil);end;function TConnectionPools.CreateNewInstance: TADOConnection;varp: PRemoteConnection;beginResult := nil;FCriticalSection.Enter;tryNew(p);p.Connection := TADOConnection.Create(nil);p.Connection.ConnectionString := ConnectionString;p.Connection.LoginPrompt := False;tryp.Connection.Open(DataBaseUser,DataBasePass);exceptp.Connection.Free;Dispose(p);Exit;end;p.InUse := True;FConnList.Add(p);Result := p.Connection;finallyFCriticalSection.Leave;end;end;destructor TConnectionPools.Destroy;vari: Integer;beginFCriticalSection.Free;for i := 0 to FConnList.Count - 1 dobeginPRemoteConnection(FConnList[i]).Connection.Free;Dispose(FConnList[i]);end;FConnList.Free;CloseHandle(FSemaphore);inherited Destroy;end;function TConnectionPools.GetLock(Index: Integer): Boolean;beginFCriticalSection.Enter;tryResult := not PRemoteConnection(FConnList[Index]).InUse;if Result thenPRemoteConnection(FConnList[Index]).InUse := True;finallyFCriticalSection.Leave;end;end;function TConnectionPools.LockConnection: TADOConnection;vari: Integer;beginResult := nil;if WaitForSingleObject(FSemaphore, Timeout) = WAIT_FAILED thenraise Exception.Create('服务器忙,请稍候再试');for i := 0 to FConnList.Count - 1 dobeginif GetLock(i) thenbeginResult := PRemoteConnection(FConnList[i]).Connection;Exit;end;end;if FConnList.Count < MaxCount thenResult := CreateNewInstance;if Result = nil then { This shouldn't happen because of the sempahore locks }raise Exception.Create('Unable to lock Connection');end;procedure TConnectionPools.ReleaseLock(Index: Integer;var Value: TADOConnection);beginFCriticalSection.Enter;tryPRemoteConnection(FConnList[Index]).InUse := False;//Value := nil;ReleaseSemaphore(FSemaphore, 1, nil);finallyFCriticalSection.Leave;end;end;procedure TConnectionPools.SetConnectionString(const Value: string);beginFConnectionString := Value;end;procedure TConnectionPools.SetDataBasePass(const Value: string);beginFDataBasePass := Value;end;procedure TConnectionPools.SetDataBaseUser(const Value: string);beginFDataBaseUser := Value;end;procedure TConnectionPools.UnlockConnection(var Value: TADOConnection);vari: Integer;beginfor i := 0 to FConnList.Count - 1 dobeginif Value = PRemoteConnection(FConnList[i]).Connection thenbeginReleaseLock(i, Value);break;end;end;end;initializationConnectionPools := TConnectionPools.Create;finalizationConnectionPools.Free;end. 来自:dirk, 时间:2004-4-2 10:17:32, ID:2535173呵呵,我喜欢直接用API做临界区:CS:TRTLCriticalSection;InitializeCriticalSection(CS);EnterCriticalSection(CS);LeaveCriticalSection(CS);DeleteCriticalSection(CS);其实TCriticalSection也就封装了这几个函数。另外想问一下楼上,用了临界区为什么还要用WaitForSingleObject?好像都是维持临界状态,WaitForSingleObject怎么用我还不太明白,望指教一下。 来自:沙隆巴斯的主人, 时间:2004-4-3 10:45:29, ID:2537506to vcanddelphi:原来使用ADO遇到过一些什么BOF、EOF错误,但D6以后就没有遇到过。抄了段文章给你。ADO:Active数据对象(ActiveDataObjects):ADO实际是一种提供访问各种数据类型的连接机制。ADO设计为一种极简单的格式,通过ODBC的方法同数据库接口。可以使用任何一种ODBC数据源,即不止适合于SQLServer、 Oracle、Access等数据库应用程序,也适合于Excel表格、文本文件、图形文件和无格式的数据文件。ADO是基于OLE-DB之上的技术,因此ADO通过其内部的属性和方法提供统一的数据访问接口方法。来自:沙隆巴斯的主人, 时间:2004-4-15 8:21:53, ID:2559846池技术是由CACHE技术发展而来,是运用非常广泛的提高性能的手段(你CPU里有一级二级CACHE,硬盘上有CACHE。。。),是典型的空间换时间策略。 来自:liuxiangsoft, 时间:2004-4-15 9:58:27, ID:2560136param:i am sorry!!!!!!!!!!!!!!!真對不起﹐我那天搞了一天的池﹐頭都搞大了﹐最后在demo里找到那個東東﹐才舒了一口長氣﹗可是我又有一點不明白﹕我用了池后﹐在type library里所定義的事情和屬性都不能編譯了﹐在池(pool)里提示說沒有定義這個對象。請問﹕是不是在type libaray里定義的屬性或事情也要在pool再聲明一次﹖如果是﹐那又怎樣聲明呢﹖ 来自:zhanggeye, 时间:2004-4-23 10:25:55, ID:2576167什么是假三层?所谓真正的MT只使用接口,哪么你是否知道DataSetProvider数据处理的实质是什么?它依然是接口!只不过在客户端用大家习惯了的DataSet模式来封装数据。这种承上启下的技术,带来的效益是很明显的。自己采用自己的接口封装及传递数据和它又有什么两样?在这个层面上,ClientDataSet和二层上的DataSet能相提并论吗?大家好象对三层客户端采用ClientDataSet深恶痛绝的样子,但我认为,批判一种技术,至少基于理解的程度上,而不是人云云。就如你在动物园内见到的狼,请不要用狗的标准去评论它,因为你没见过在荒野里真正的狼。来自:zhanggeye, 时间:2004-4-26 12:08:51, ID:2581036to 沙隆巴斯的主人:采用DataSetProvider和ClientDataSet来开发就是围绕着数据库吗?建议你看看李维的 ADO/MTS/COM+高级程序设计篇。在业务对象内,DataSetProvider提供数据操作接口,完全符合三层体系的设计原则。DataSetProvider只是业务对象中数据接口部分,一个完整的业务对象还会包括其它接口和业务逻辑。把它们对立起来是完全没有道理的。ClientDataSet只是向业务对象的接口取得和提交数据,并在客户端采用DataSet的模式来封装,这样客户端就可以依旧采用数敏控件来进行开发,对于大量的现有资源直接就可以在三层开发中应用,而原来两层的部分优势也得到保持。这本来就是delphi高效开发三层的优点所在。(当然,只能用DELPHI开发客户端才有这种优势.)说李维的三层是假三层,是因为没有分清ClientDataSet的数据处理实质是接口,是中间层的业务对象,而不是原先的DataSet哪样面向操作数据库。来自:proman, 时间:2004-4-26 19:41:45, ID:2582202TO: zhanggeye,所谓多层,按照众多的说法,无非是表现层,业务层。数据驱动层,数据库等,其中还可以根据系统的实际情况加N多层。从理想的情况下来说,我们希望的多层结构的表现层,也就是客户端,无论这个客户端是一个智能客户端中还是在IE中,我们希望它它后面的业务层或其它层打交道时,不能涉及到数据库中的结构。一旦涉及到数据库的结构,理想中的多层就会削弱。如果CLientDataSet传的是数据库中的某张表,或是某个查询,毫无疑问,这样的多层是要打折扣的。可是实际系统中,我们遇到的问题,我们是否有足够的人手来开发真正的多层系统,各个层之间用完善接口进行联接。实际是很多系统没有足够的人手来进行这样的开发,那么取而代之用ClientDataSet直接传递数据库中的表也就不足为奇了。虽然这样与多层的思想是不相同的。但是在资源充分的情况下,我仍然建议大家尽可能的用接口将各个层之间划分清楚,这确实程序发展的方向,也为写出质量更佳的程序打下了坚实的基础。李维的书是没有问题的,他提出了一个利用较少的资源来快速开发一个三层结构的方法,这确实是一个三层。但是也许他不是纯的三层的概念。可是他的开发代价确很小,我认为李维是用这样的简单方法把大家引入多层的概念。可是如果大家仅仅停留在这里,那显然是是只知道是什么,而不知道为什么。我也是看李维的书学习三层的。但是在我的三层的系统根本就没有用过DataProvider,当然由于资源有限,我也用了ClientDataSet传一些数据。所以说大家不要在Provider的上面浪费太多的时间,不需要Provider仍然可以传数据。我更建议不要用Provider,Provider纯是封装数据方法。而不用Provider,你可以更侧重于封装业务功能,这可能更好吧。看大家讨论了很长时间,我觉得争论的有点没有意义,真正的掌握多层系统的开发思想才是最重要的,这一点,我想我们都是可以互相学习的。 来自:zhanggeye, 时间:2004-4-26 20:59:32, ID:2582313to proman:我很赞同你的看法,而我更认为,既然是一个delphi程序员,就要充分利用delphi的优势。最简单的一种情况,一个销售票据对象向客户端提供一个货品清单接口,自己采用数组封装的效率就远不如采用dataset来得好。midas是三层上一种高阶的开发机制,不用它当然也能写三层,但用了它则可以轻松的写出三层。DataSetProvider并不只是纯粹的封装数据,你完全可以在它上面封装业务规则。纯三层的理念有时只是一种理想状态,需求的变化及业务规则的变化往往会导致数据库结构的变化,这些变化有的最终通过接口反映到客房端,并不会因为你采用自己的接口客户端就不用改了。而采用DataSetProvider/ClientDataSet,这些变化更能直接的反映到客户端。退一步来说,采用自己的接口也可以用ClientDataSet的模式来封装数据,为客户端开发提供便利。三层的开发模式灵活多样,更多的是要从需求出发,理论的东西不要死套硬用,就如你所说:”真正的掌握多层系统的开发思想才是最重要的“。我也觉得这种争论没什么意义,一直也不参与,只是对大家后来的评论实在难以认同,随便说几句而以。你大可聊作一笑。 来自:proman, 时间:2004-4-26 21:52:55, ID:2582404补充一句,我认为Borland的三层技术中MIDAS的核心实际不是什么接口或是Provider而是ClientDataSet中Data的数据封装,也就是如何封装一个数据集通过网络传输,它封装的数据结构是不公开的,这也是Midas的核心技术。而MIDAS的是否收费就是根据这个数据是否在网络上传输来决定的。 to infowain:我公开的代码只是两个基本的类的代码,其实还有操作员类(TOperator)和操作员池(TOperatorPool);用户(TUser)和(TUserPool);价格(TPrice)和价格池(TPricePool)等等。因为和具体业务相关,不大方便帖出来的。我自己在用DELPHI做的开发中,是坚决不用任何的Provider,但还是用了ClientDataSet,那是为了出报表,很无奈——实在是不想再去设计一个RecordSet了;再说,报表本身的性质与一般的业务对象相比也是有很大不同的。 来自:yangying_2000, 时间:2004-4-26 23:21:03, ID:2582497没想到还有吵这个的,先说我的立场,我支持三层,另外还有些要说的1.某些人认为两层比三层效率要高,这个不敢苟同,确实,少了一个中间层,对client来说好象要少处理很多东西,其实不然,client对数据存取的代码放到了应用服务器,一般来说应用服务器性能好,因此,对于单个的查询的响应时间上来说应该差不多.其实效率高低主要看服务器的处理方式上,假设100个client同时上线,对于C/S来说,数据库会开100个会话,对于多层来说,数据库只会开应用服务器所请求的对话,一般来说不会超过10个,而100个client就分别把自己的请求交给应用服务器,应用服务器对请求排队,在自己的10个会话中处理完毕后再把结果返回给client,也就是说在C/S下,当client没有任何请求的时候,这些会话是停在那不做任何处理的,必须等client发出请求该数据库会话才会得到利用,而实际情况是大部分client的数据请求并不频繁,因此造成了浪费,所以从这个角度来看三层要好些.2.有些人觉得所谓的伪三层不好,我倒恰好觉得非常好,因为那是用来替代两层结构的必须品,以现在的DELPHI,如果说做两层结构,真的不如使用伪三层,道理很简单,开发的过程几乎没有区别,但是伪三层的client的维护要小很多,两层需要装数据库客户端,需要定义数据库客户端别名,如果用BDE则还要装BDE,如果使用ODBC则还要定义ODBC的别名,这些环节一旦有问题,整个客户端都不能用,而伪三层则只要有执行文件和MIDAS.DLL即可,如果你用ASTA来做伪三层,连动态库midas.dll都省了,拷过去就能用.因此伪三层可以说是替换两层结构的利器.相比伪三层,两层结构几乎可以说没有任何优势可言.来自:zhanggeye, 时间:2004-4-27 0:37:23, ID:2582573李维的书好不好,我不想评论,但只凭是否使用DataSetProvider/ClientDataSet来断定李维的三层是假三层是不能接受的。DataSetProvider/ClientDataSet是delphi独有的优势,如果不充分利用,使用delphi开发三层和使用其它语言开发基本没有什么不同。认为ClientDataSet 使用后台数据库透明化了其实是一个误解,ClientDataSet只是从一个接口中取得了数据,接口内部来源是不知道的,可能是一个表,也可能是一个视图,更合理的做法是封装了业务逻辑后的一个结果数据集。因为大家经常同一个人开发客户端和中间层,所以理所当然的知道哪个DataSetProvider来源于什么,中间做了什么业务逻辑。如果中间层不是你开发的,只是告诉你接口接供什么功能及数据,这些数据采用ClientDataSet格式封装,哪么你还能知道后台数据库是什么类型,它的表名是什么,它是直接从数据库取得还是中间层自己合成的。这些都是不可能知道的。客户端只要对ClientDataSet进行处理,实现界面逻辑就可以了。所不同的是,这时你完全可以使用数敏控件进行可视化设计。这有几种开发工具能做到?还是proman说的哪句话:”真正的掌握多层系统的开发思想才是最重要的“。理论是死的,不能不切实际的照搬。比如在业务对象的粒度控制上,所谓“真三层”是要求分得细,分得清。如果是一个多用户高并发的应用。这是非常有优势的。但对于中小型应用,这些优势收益远不如开发及维护成本的支出。一个胖中间层可能是更好的选择。说这么多,有点象在为李维辩护了。所以有必要点一下,本人菜鸟一个,跟李维一丁点也不认识。粗读了他几本书,仅此而以。 来自:阳冬飞雪, 时间:2004-5-5 9:51:21, ID:2594220其实程序的执行效率与程序设计有很大的关系,有的人设计的三层比两层快,有的人设计两层比三层快,按照简单的连接和网络数据流两层较三层快,因为两层较直接,但若在很多的连接时,如果仅仅是为了采用三层架构而作三层设计,当然不会优越到哪里去,而要采用三层的思想.其实三层或多层结构设计提出了一种比较好的设计理念,当我们在作过三层系统开发之后,用其理念作两层开发也比自已以前作的两层开发效率要高.在我身边就有过类似的例子,一个同事做的三层开发几乎要将网络拖跨,原来他在设计程序时对数据在网络中的流向问题处理不当,这样即使用三层又怎么样呢,只用了其架构而没用其内函,就象用马拉小车一样,当然没有用马直接拉一个木板车快,也许这个比喻在这里并不恰当,但大多数人在用过三层作系统开发之后会有不同的感受,但我个人认为三层和两层各有优劣.在不同的应用环境中当然选择不同的开发方式.


    最新回复(0)