RogerTong's Tech Space

文章书傲骨,程序写春秋
posts - 15, comments - 67, trackbacks - 0, articles - 0

2008年7月9日

这是一个曾被热烈的讨论过的问题。之所以忽然谈起这个,还得从我们今天的一个项目验收中我们与客户的程序员之间的争议说起。我们的项目采用的是常见的三层架构:DataBase - ApplicationServer - Client,在ApplicationServer中放置了鉴权、运算、指令路由、通信服务、Callback等方面的业务逻辑代码,并对客户端公布相应的调用契约(接口)。在客户验收的过程中因为一个失败的数据库操作服务引申到客户方的程序员对于N层架构的质疑。该程序员令人咋舌的观点是:“N层架构惟一的忧势是可以省去一些数据库调用上的License"。——这番话不由得让我想起来我当年写的一套很肤浅的DELPHI N层操作框架(其实那套框架用于快速开发还是不错的),1999-2000年时,DELPHI中N层架构设计的思想刚刚起步,而在当时根本不知业务逻辑为何物的我抱着一种无知者无畏的心境快速的推出了我的DELPHI“三层框架”,其实这个所谓的框架中,中间层什么也没做,只是充当了一个SQL相关数据操作的一个通道转接的作用,而相关的业务计算过程全都在客户端进行……



还记得1999-2002年,多层设计要不要采用在大富翁论坛上屡屡被提起,但总是没有什么下文。那是一个软件架构设计思想刚刚开始升华的年代。随着COM/DCOM/MTS/CORBA等多项分布式技术的不断发展成熟,传统C/S架构的生存还是毁灭,各自都有一派忠实的拥趸者。而什么是分布式架构?分布式架构中商业智能与数据持久的代码如何切割?各层之间关系的维护及代码精细粒度的控制都是软件架构师们在很长一段时间的疑惑。软件设计的思想总是不断向前发展,而我们的疑惑也在慢慢的解开。不得不提提那句开发界的名言:“开发环境及开发语言决定了开发模式”(大意是这样吧),说到这里不能不说对Borland Delphi及C++Builder的失望,曾经RAD开发的王者,却没能借此优势形成相应的开发模式及社区氛围,而过份对RAD的倚重只是为他培养了一堆只会拉拉控件连数据库的肤浅的程序员。其引以为傲的VCL中一切以控件为中心,完全忽略基础设施的建设——像单双链表,List,队列,集合之类的一些最基本的数据结构的元素都没有提供给使用者。从这些元素上去考虑,DELPHI之类的一些RAD工具走向没落不是没有原因的。



说了一大堆的废话,我们再来具体谈谈N层设计应该是怎样的一回事。当然,这个只是我同圈里一些朋友的观点,我们欢迎有不同的声音。先来说说业务逻辑,什么是业务逻辑?以数据库的系统来说,在我们看来,软件架构中所有与数据运算、数据判断、数据持久等方面相关的操作都属于业务逻辑的范畴。举例来说,如果我们客户端需要登陆到系统,那么登陆的过程便是一个业务逻辑,可以在应用服务层开放一个帐号管理的接口,如:IAccount,登陆只是该接口中的一个方法,如:Login(UserInfo userInfo),对于客户端而言。我们只需要传递相应的一些登陆信息到应用服务端,而应用服务端如何验证登陆,这个则不是客户端需要关心的内容。应用服务端向数据库发出怎样的查询指令,写入怎样的信息这个都是应用服务端的事情。客户端需要知道的仅仅是,应用服务端有这样的一个调用契约,成功与否,都会有相应的一些约定信息返回。就如同我们通过COM调用Excel一样,我们并不需要关心相应的读写操作是如何进行的,我们只需要知道有相应的调用接口即可。



分层设计的意义是什么?为什么要面向接口开发?先来讲讲面向接口开发。我觉得这除了维护软件本身更好的架构外,也可以将一个开发小组中开发人员的职能分离:在项目开始的时候,项目小组的成员先依据用户需求定义出具体的接口规范。然后安排相应的人员对相应的接口作出实现,并作UnitTest,在最后的环节再完成代码的拼合,甚至设计出开发小组的daily builds(每日构建)服务。从这个角度上来讲,面向接口开发可以很好的调配开发小组成员,让项目经理可以依据不同成员的能力、任务来控制项目开发的日程。那么分层的意义是什么呢?前面也大约提到是分离业务逻辑,就上面的例子来说,如果我们将登陆的过程放在客户端,一旦登陆的规则发生改动,那么我们所有的客户端程序就都需要更新,再者,您放心直接给客户端的机器访问数据库吗?安全隐患不言而喻,而如何我们将这个逻辑放置在应用服务端,则无论是在后期的应用规则修改还是数据安全的系数上,都会有一个较大的提升。


写得比较凌乱,大家凑合着看吧。

posted @ 2008-07-09 16:42 RogerTong 阅读(749) 评论(2) 编辑

摘要: 熟悉Castle的朋友一定清楚:在Castle体系中,有一个叫Aspect#的子项目。Aspect#是一个AOP的框架,在这篇文章中,我并不打算直接开始讲Aspect#,因为Aspect#的封装过于完善,不太利于用来讲述AOP的具体实现细节。其实Aspect#的基础是 DynamicProxy,所以,我们先来讲讲DynamicProxy。

在很多朋友的眼中,AOP似乎是一个很神秘的东西,其实在我看来,AOP的本质就是对一切操作行为的拦截,在.Net中,AOP的本质更简单,纯粹是对方法的拦截。为什么这么说呢?因为在.Net中,一切的操作都是方法调用:事件的 “+=” 及 “-=”最终被转换为 Addxxxx(...),Removexxxxx(...)类型的方法;属性的 get/set 过程也被转换为 getxxxxxx(...),setxxxxxx(...)类型的方法;而方法的本身就更不用说了……阅读全文

posted @ 2008-07-09 16:42 RogerTong 阅读(2568) 评论(3) 编辑

摘要: 在上一篇文章中,我们讲过如何从外部调用插件项目,接下来我们来看看插件项目与插件项目之间的调用。

我们来看看现在的应用场景:同样是对两个数值进行运算,MathOne是基本并且稳定的运算服务,负责对这两个数据相加并返回值。但是由于新的用户的需求发生了改变,他们希望获得的值是MathOne结果的平方数,例如:

如果我们传入的值是 (2,3),旧用户期望得到的值是 2+3 = 5 ,而新用户期望的值是 MathOne 运算结果的平方数,即是 5 2 = 25。

由于需要同时考虑新的需求并兼容旧的用户,所以我们需要加多一个插件项来为新用户响应需求。

为了让大家看起来不那么吃力,我们还是重述一下相关的接口及 MathOne 的实现阅读全文

posted @ 2008-07-09 16:40 RogerTong 阅读(219) 评论(0) 编辑

摘要: 在之前的文章中,我们已经了解了Mussel插件项目树的结构及插件简单的制作方式,今天我们来看看如何对Mussel的插件项目进行调用。

我们想像一下有这样的一个应用场景:我们需要对两个数值运算并返回一个结果,这个运算交由一个独立的运算过程来进行,我们并不关心运算的具体细节,我们只需要运算过程返回一个结果,先来看看这个运算接口的定义阅读全文

posted @ 2008-07-09 16:39 RogerTong 阅读(132) 评论(0) 编辑

摘要: 在之前的文章中我们提到了Mussel插件的层次结构:插件(Addin)包括了插件项目的装载节点(AddinNode),装载节点又包括了插件项目(AddinItem),而每个插件项目又可以包括无限下级的子项目:

Mussel的加载器在加载时,会尝试读取当前文件夹以下(包含子文件夹)的所有*.addin文件,每个addin文件被Mussel加载器识别、加载后,便会形成如上图所示的IAddin对象,如果每个插件都是独立的,这个过程就非常简单。但是插件之间必须然会有一些联系,例如,如果系统的工具栏是一个稳定的插件(addin),如果金山词霸有一个扩展,能添加一个按钮到系统的工具栏中,我们先来看看摸拟的配置文件,为了方便说明,我们假定 ClassKey指示的类型都存在并忽略掉程序集引入的配置部分。阅读全文

posted @ 2008-07-09 16:26 RogerTong 阅读(176) 评论(0) 编辑

摘要: Mussel插件项目都从AddinItem继承,AddinItem类是所有插件项目的基类,附合IAddinItem接口规范,我们先来看看IAddinItem接口的构成类图:

上图是IAddinItem接口的全部方法与属性,从前三个属性中,我们不难看出Addin插件树的组成方式,可以想像的是AddinNode中自然还包插了IAddin的对像。

Properties属性是插件加载时,从Addin配置文件中读取的对应的AddinItem的配置信息。而通过this属性可以轻松的访问到当前插件项目的子插件项目……阅读全文

posted @ 2008-07-09 16:11 RogerTong 阅读(224) 评论(0) 编辑

摘要: 在前面的文章中,我们说过,Mussel的内核是一个插件容器。Mussel本身不提供任何功能上的服务,所有的功能都由插件来提供。Mussel负责协调插件与插件、插件与容器以及容器与调用者之间的通信。

Mussel提供了一个装载器,这个在以后的文章中会讲到。装载器的Start方法调用时,装载器遍历Mussel程序集所在文件夹以及所有的子文件夹,搜寻 *.addin 的插件配置文件。在这里,我们先看一个addin文件的演示……阅读全文

posted @ 2008-07-09 15:42 RogerTong 阅读(300) 评论(1) 编辑

使用.Net开发已经有近三年的时间了,深刻感受到.Net给软件开发思想带来的巨大的变化。.Net一些独特的语法元素(如特性、委托等等)以及对线程、Domain等概念的完美的支持,无疑为软件架构师提供了巨大的设计优势。


从使用.Net的第一天起,我就一直期望能设计一个符合小组使用,通过简单配置即可协调好程序集之间相互作用的一套轻量级框架,在对Castle、PostSharp、NHibernate等项目的逐渐了解及熟悉后,这种期望越来越强烈。于是开始构思,规划Mussel的发展路线。在为历经N个版本的更替,Mussel目前亦开始有一个基本的雏型。


Mussel被设计为一个插件管理器,内核部分不提供任何服务,只是一个插件装载/卸载程序。插件是可以树状加载的,即是插件还可以包括子插件集合。并且可以指定插件加载的AppDomain,插件之间通过接口契约来相互访问,对于跨网络的插件调用,可以通过AOP的技术,为相应的插件接口来产生远程Proxy。


Mussel的Core部分还将同时支持Compact Framework及标准的.Net Framework,当然,由于Compact中的AppDomain的实现方式同桌面版本有非常大的不同,并且Compact中不支持AOP的技术,所以Mussel.Core的CF亦版本不支持跨AppDomain及AOP操作,但是基本的功能与桌面版本并无差异。

posted @ 2008-07-09 15:32 RogerTong 阅读(408) 评论(0) 编辑