{学习编程} 糟糕的师傅,蹩脚的徒弟

27 views
Skip to first unread message

莫华枫

unread,
Dec 9, 2008, 1:59:52 AM12/9/08
to pon...@googlegroups.com

我见到过一种三层模型,分为UI、BLL、DAL。此外还有一组Model类,用于封装实际的业务对象。这没什么特别的,多数三层模型都是这样。但是,这种三层模型有个特殊的地方,对于每一个描述业务对象的Model类,在BLL里基本上都有一个类对应,实时查询、检索等等操作。差不多就是个管理类吧。但如果深入到这些类内部,发现它们只是一组wrapper。其内部保有一个对象,该对象的类来自于DAL。而BLL中的这些类上的操作在DAL相对应的类上都有。BLL类实际只是在转发操作,而真正工作的,是DAL中的那一组类。

我觉得很奇怪,为什么要凭空锻造出这么一组毫无用处的类呢?得到的答案是:从MS的案例中学来的。诧异之余,我未免心中起疑,MS再差劲,也不至于直眉瞪眼地破坏DRY吧。肯定是这些家伙学艺不精,会错MS的意了。

不久前,公司来了一个应聘者,走的是.net路线,在外做过几个项目。我们出编程题考他,结果他做的程序的结构也是三层模型,居然和上面所说的结构一模一样,也是在BLL中放了一组无用的wrapper。关键是,此人和那个三层模型的创造者此前完全不可能相识。那么,只有两种可能。要么是他们以同样的方式错误理解了MS的案例;要么就是MS的案例本来就是这样的。

MS的案例究竟如何,我没有考证过,也无意去关心。我想到的是一个程序员如何开始学习编程。很多程序员喜欢从案例开始学习。从抄写代码,慢慢变为小修小改,然后逐步理解,最后开始单飞。但是,这里有个问题。程序员就像小鸟。小鸟会把第一眼看见的会动的东西当成妈妈,而程序员则会把最初学习的案例当成典范。

因而,程序员最初开始的编程学习就显得非常重要。一旦定型,以后很难改掉。一个程序员最初不应该从某些应用型的案例中开始学习。更多地应该把精力放在基本功上,像基本的编程原则、算法、范式等等。并且从一些小案例开始,比如写个小算法、小工具等等。此时的程序员还缺乏判断力,无法认清那些复杂案例中的问题和陷阱。上面提到的这种三层模型,我相信ms原意并非如此。但是,涉世不深的程序员很容易误解其中的用意,然后加以发挥。在没有正确引导的情况下,误入歧途。

可是,现实往往是残酷的。很多程序员并没有机会获得完善的培训。他们在大学里没有打好基础,来到工作单位就立刻投入开发工作,很多都是从接手和维护其他人的代码开始。企业希望以此快速地培养出程序员。但是这也往往使得那些代码中原本存在的缺陷被新手们充分吸收。老程序员往往无暇顾及那些新人。有时,甚至他们自身也未曾意识到存在的问题。这样,错误就一代接一代地传了下去。

指出了这些问题之后,我却不知道有什么好方法能够破解。我能想到的唯一方法就是,趁自己工作前,或者技术定型之前,打好基础,开拓眼界。不要迷信案例,带着怀疑的眼光和基本原则来看待他们。多问(不是在这里哈),多想,多google,多wiki,还有多上TopLanguage。:P


--
反者道之动,弱者道之用
m...@seaskysh.com
longsh...@gmail.com
http://blog.csdn.net/longshanks/

bronco

unread,
Dec 9, 2008, 2:11:14 AM12/9/08
to pon...@googlegroups.com
"BLL类实际只是在转发操作,而真正工作的,是DAL中的那一组类"
我觉得这样做很好啊,当DAL引擎由DAL1变到DAL2的时候UI代码不用发生任何变化;

1:第3方库先封装一下自己的库,以后我就可以很方便的换lib了
2:policy 也是这样随便换啊;

一层转发 inline 一下 基本没有任何代价,给维护带来了很大的方便啊。
为什么不wrapper呢?

莫华枫

unread,
Dec 9, 2008, 2:24:18 AM12/9/08
to pon...@googlegroups.com
是这样,Model类代表着业务对象。每个model类有一个BLL类对应,起管理作用。但请注意,这里BLL中的类和DAL中的类的接口是一模一样的,BLL只是转发了操作。当DAL发生变化,BLL也相应地发生变化。这里根本的问题是,如果DAL类的接口和BLL一样,就表明有业务相关的内容进入DAL了。
实际上,这个三层模型把应该放在BLL中的东西放到了DAL中,而按规定,UI不应当越过BLL直接访问DAL,那么为了让UI得到服务,就不得不在BLL中放置包装类转发操作,以免造成跨层访问。
这样的做法造成的后果就是,无论是BLL变动,还是DAL变动,都将造成两个层面一起修改。

2008/12/9 bronco <renfe...@gmail.com>

pongba

unread,
Dec 9, 2008, 2:28:46 AM12/9/08
to pon...@googlegroups.com


2008/12/9 莫华枫 <longsh...@gmail.com>

可是,现实往往是残酷的。很多程序员并没有机会获得完善的培训。他们在大学里没有打好基础,来到工作单位就立刻投入开发工作,很多都是从接手和维护其他人的代码开始。企业希望以此快速地培养出程序员。但是这也往往使得那些代码中原本存在的缺陷被新手们充分吸收。老程序员往往无暇顾及那些新人。有时,甚至他们自身也未曾意识到存在的问题。这样,错误就一代接一代地传了下去。

关于"错误就这样一代一代传了下去"我这里还有一个通俗例子,其原因与程序员行业也许是很相近的,都是由于没有带着质疑的眼光去审视一个方案,弄清它从Problem Solving角度到底是从什么需求出发,解决什么问题(以及自身引入什么问题——往往),适用什么场合,这样就导致错误地认为这个方案是放之四海而皆准的,或者至少不明白方案的适用场合是什么,在具体问题上逮着一点相似性就往上生搬硬套,设计模式里面这类例子应该是尤其常见的,有这方面具体经验的老大们不妨举几个例子。

我看到的一个故事是这样的:

The Dinka and Nuer tribes of the Sudan have a curious tradition. They extract the permanent front teeth of their children—as many as six bottom teeth and two top teeth—which produces a sunken chin, a collapsed lower Up, and speech impediments. This practice apparently began during a period when tetanus (lockjaw, which causes the jaws to clench together) was widespread. Villagers began pulling out their front teeth and those of their children to make it possible to drink liquids through the gap. The lockjaw epidemic is long past, yet the Dinka and Nuer are still pulling out their children's front teeth.

A practice that makes no sense at all to outsiders makes perfect sense when seen through the lens of dissonance theory. During the epidemic, the villagers would have begun extracting die front teeth of all their children, so that if any later contracted tetanus, the adults would be able to feed them. But this is a painful thing to do to  children, especially since only some would become afflicted. To further justify their actions, to themselves and their children, the villagers would need to bolster the decision by adding benefits to the  procedure after the fact. For example, they might convince themselves that pulling teeth has aesthetic value—say, that sunken-chin look is really quite attractive—and they might even turn the surgical ordeal into a rite of passage into adulthood. And, indeed, that is just what  happened. "The toothless look is beautiful," the villagers say. "People who have all their teeth are ugly: They look like cannibals who would eat a person. A full set of teeth makes a man look like a  donkey." The toothless look has other aesthetic advantages: "We like the hissing sound it creates when we speak." And adults reassure  frightened children by saying, "This ritual is a sign of maturity."15 The original medical justification for the practice is long gone. The  psychological self-justification remains.

引自上次介绍的《Mistakes were made(but not by me)》

--
刘未鹏(pongba)
Blog|C++的罗浮宫
http://blog.csdn.net/pongba
TopLanguage
http://groups.google.com/group/pongba

Jay True

unread,
Dec 9, 2008, 2:59:52 AM12/9/08
to pon...@googlegroups.com
2008/12/9 莫华枫 <longsh...@gmail.com>

是这样,Model类代表着业务对象。每个model类有一个BLL类对应,起管理作用。但请注意,这里BLL中的类和DAL中的类的接口是一模一样的,BLL只是转发了操作。当DAL发生变化,BLL也相应地发生变化。这里根本的问题是,如果DAL类的接口和BLL一样,就表明有业务相关的内容进入DAL了。
实际上,这个三层模型把应该放在BLL中的东西放到了DAL中,而按规定,UI不应当越过BLL直接访问DAL,那么为了让UI得到服务,就不得不在BLL中放置包装类转发操作,以免造成跨层访问。
这样的做法造成的后果就是,无论是BLL变动,还是DAL变动,都将造成两个层面一起修改。

你的意思是不是在这种情况下,BLL 类就是没有必要的,而 UI 应该直接访问 DAL 的方法?

莫华枫

unread,
Dec 9, 2008, 3:19:53 AM12/9/08
to pon...@googlegroups.com


2008/12/9 Jay True <gla...@gmail.com>

不不不,是本该放在BLL中的东西放到了DAL中。放回去不就完了嘛。:)
DAL只需要处理数据访问的东西,比如屏蔽数据库的差异、数据访问用户端的差异、控制transaction等等。
三层模型的分法有很多种,但如果在两个层中出现相同接口的类肯定是有问题的。

lxcypp

unread,
Dec 9, 2008, 3:32:07 AM12/9/08
to pon...@googlegroups.com
看来老莫对这种知其然不知其所以然的程序员风格深恶痛绝啊。。。

Jay True

unread,
Dec 9, 2008, 3:35:19 AM12/9/08
to pon...@googlegroups.com
呵,好像一般那种教程中的例子,根本就没有什么东西可放到 BLL 中的,但为了教学目的又不能没有,所以初学者能看到的也就都是这种 wrapper 了。

2008/12/9 莫华枫 <longsh...@gmail.com>

赖恒坚

unread,
Dec 9, 2008, 3:46:59 AM12/9/08
to pon...@googlegroups.com
我倒是同意这个,"好像一般那种教程中的例子,根本就没有什么东西可放到 BLL 中的"
当然把"本该放在BLL中的东西放到了DAL中"是不对的,但确实存在这种没什么东西可放在BLL中却需要BLL存在的情况

也许比较像什么逻辑都没写的getter和setter

Jay True

unread,
Dec 9, 2008, 4:19:17 AM12/9/08
to pon...@googlegroups.com
呵,应该说不只教程中的例子,我以前在参加学校外包项目的时候(好像是 B2C 来的),就是整天在那边写这种 wrapper 。

2008/12/9 赖恒坚 <hama...@gmail.com>

bronco

unread,
Dec 9, 2008, 4:21:50 AM12/9/08
to pon...@googlegroups.com
这个一对一的wrapper本身没错,但别把这个一对一的wrapper做为BLL对DAL的wrapper

li li

unread,
Dec 9, 2008, 4:27:44 AM12/9/08
to pon...@googlegroups.com


2008/12/9 莫华枫 <longsh...@gmail.com>

因而,程序员最初开始的编程学习就显得非常重要。一旦定型,以后很难改掉。一个程序员最初不应该从某些应用型的案例中开始学习。更多地应该把精力放在基本功上,像基本的编程原则、算法、范式等等。并且从一些小案例开始,比如写个小算法、小工具等等。


这句话,深感认同!

莫华枫

unread,
Dec 9, 2008, 8:19:45 AM12/9/08
to pon...@googlegroups.com
实际上,三层模型我看到过两种分法。一种是将业务逻辑对象放在BLL中。业务逻辑对象包含相应的属性和方法。比如学生就包含学号、姓名、班级等等。其中学号、姓名等是简单对象(字符串等等),而班级等则是其他对象的引用。这样,业务对象拥有了业务对象间的逻辑关系,同时还赋予它各种相关的约束,比如如果将一个日期赋予学生对象的出生日期属性,那么学生对象会检验是否有效(不能是公元前、不能是为来的日子等等)。BLL层同时还用有了业务对象的管理模块,负责业务对象的查询、检索、增删改等等。UI层访问BLL的这些业务对象和管理器,执行相应的业务流程和输入输出。有时候,也会进一步把UI分成Present层和Workflow层。DAL实际上是一个负责数据库访问的抽象层,用以处理数据库有关的操作。
另一种分法就是上面提到的这一类。DAL实际上负责对象和关系数据库之间的转换,差不多就是orm的作用。按理说,BLL中则应当是业务对象管理器,负责查询、增删改。但是,由于DAL执行了orm操作,已经包含了大多数对象管理器的功能,否则orm将无法进行。原则上,DAL只负责对象的增删改和提取,更进一步的查询、访问、约束控制都在对象管理器中执行。但实际上这样的划分只会造成性能的下降,并且使得系统复杂化。既然DAL中已经拥有对象管理器所需的所有信息,那么直接把对象管理器置于DAL层更加合理。
另一方面,理论上,三层模型是为了提高系统的灵活性,把系统的变化局限在某个层次。但这仅仅是理论上的。要发挥三层模型的最佳效能,最好是上层的变化比下曾更大。但在实际中,反倒是一些基础的东西,比如业务类的结构,逻辑关系,变化的很多。同时,业务逻辑和UI的关系也比预想的紧密。因而,很多原本BLL的操作、约束被移到UI,从而造成BLL的空置。
在这种情况下,应当果断地作出调整,而不是教条地遵照教材或案例的指示。或者将DAL的主要内容上移至BLL,让DAL仅仅负责数据库访问管理。或者索性将BLL砍掉,只保留两层。如果还是生搬硬套地在BLL中安置wrapper,只会造成不必要的无效劳动。
程序员永远应当记住一些基本的编程原则,比如DRY。如果发现教材、书本、案例中的东西同DRY之类的原则发生了冲突,那么要么是教材错了,要么是程序员理解错了。
另外,应该认真地读一下上面pongba的回帖,里面包含重要的信息。不多说了,读回帖去了。:)

莫华枫

unread,
Dec 9, 2008, 8:31:03 AM12/9/08
to pon...@googlegroups.com
还是再多说两句:P。
分层并不是越多越好,应该是越少越好。实际上,三层模型中至少有一层是迫不得已。负责orm的这一层(不管它叫什么),之所以存在并非它必要。只是我们现在存在阻抗失配问题,不得不容忍这一层的存在。如果有某种技术消除了阻抗失配问题,那么最烦人的一层便可以寿终正寝了。
我的另一个帖子中up提到了XRX,这就是一种可能的层次杀手。:)

王宁

unread,
Dec 9, 2008, 5:46:25 PM12/9/08
to pon...@googlegroups.com
我对几层模型啥的一窍不通,不过恰好想到了一个有趣的事情:当年与J2EE一起蓬勃发展起来的还有PHP,(很大程度上)因为它把DAL直接塞进了UI。

讨论之前,先引一段 from The Art of Project Management, Chapter 2.2:

--------
The worst thing is to blindly follow a set of rules or procedures that are clearly not working, simply because they show up in some famous book or are promoted by a well-respected guru. More often than not, I've found that obsessing on process is a warning sign of leadership trouble: it can be an attempt to offload the natural challenges and responsibilities that managers face into a system of procedures and bureaucracies that cloud the need for real thought and action.
--------

生活和工作中,照搬永远比思考和创新要容易。尤其是,当我们对所要解决的问题缺乏自信的时候,引经据典既容易引发认同(特别是面对同样不自信的同事和半懂不懂的老板),又能省去思考和干活的麻烦。这不见得是新手才容易犯的错误,而是每个人在面对不熟悉的问题时都可能产生的一种自然的思维倾向。解药也不难,多思考多行动而已,即real thought and action。既认真思考过去的范例和经验,也审慎考察眼前的状况和问题。真正的问题是什么?有哪些约束条件?过去这样做有什么好处?有什么不足?当时的条件现在还是否满足?哪些条件变化了?如何利用变化的条件?等等。

扯远一些,这里牵涉到"学"和"知"的问题。因为教育方式的关系,许多人(包括我自己在很长一段时间内)都自觉或不自觉地把"知"当成了"学",用KNOW取代了STUDY。所以,熟读《十万个为什么》不能让你成为一个物理学家或化学家,读遍天下棋谱的人未必能成为真正的高手。又比如,一遇到排序问题,大部分科班出身的同学都能脱口而出"快速排序O(nlgn)"。但具体到某一个特定问题,针对特定类型的数据或环境挑选一个合适的排序算法,恐怕不是每一个人都能做好。这一点,读Sedgewick同学的算法书时感慨颇深。这位Knuth的得意门生写书的风格如抽丝剥茧,环环入扣,根据不同的条件分析和改进算法,个人认为是学习如何STUDY的范本。

说到底,了解并不难,学习却也不易。所以,中国的老同志说:"学而不思则罔,思而不学则殆。"

干脆再扯远一点。人多的地方就会有讨论,有讨论就难免起争执。论坛更是如此。其实如果我们时刻以"学"的心态看问题,很多争执其实并没有太多价值。什么是"学"的心态呢?我自己的总结:存疑存异。C++不一定能干掉C,就像C没能干掉FORTRAN;Linux不一定能干掉Windows,就像Windows没能干掉MacOS;Firefox不一定能干掉IE,就像IE没能干掉Netscape(还魂了!)。我个人的经验,往往是"知"而不"学"的人容易把问题绝对化真理化信仰化。正所谓"相信要到迷信,服从要到盲从"。而现实常常是复杂和多元的,每一种方法和技术都有存在的价值,以及特定的生存条件。与其固执地争论它们的优劣,不如心平气和的讨论细节,允许不同的想法在自己的头脑里和平共处。毕竟,我们是程序员而非牧师,我们需要完成任务而非普渡众生。

2008/12/9 莫华枫 <longsh...@gmail.com>

张慧聪

unread,
Dec 9, 2008, 7:35:57 PM12/9/08
to pon...@googlegroups.com
说滴真好:-)

2008/12/9 王宁 <nwan...@gmail.com>

nemo

unread,
Dec 9, 2008, 8:16:39 PM12/9/08
to TopLanguage
不知道楼主是在什么样的阶段真正领悟到“我能想到的唯一方法就是,趁自己工作前,或者技术定型之前,打好基础,开拓眼界。不要迷信案例,带着怀疑 的眼
光和基本原则来看待他们”。我是在工作了蛮长一段时间后。经历了从X86平台到嵌入式平台。MS C++到linux下的C/C++开发以后。

这个世界变化太快,特别是计算机行业,对于普通程序员大众(包括我),在很多时候是在用别人写的库,模型等等。除了一些自己真正关心的领域,其他的好像
都是不求甚解。恩拿来用,大概了解下背景知识。有时候其实这些库啊模型啊或许已经不十分适合现在实际情况。要想扩展出去深入的了解一下。却发现又是一个
宽广的天地。。时间和精力啊。。

我们也积累了一大堆老的存在各种问题的代码,不过采取了一个不是办法的办法。目前基本上新进来的人都是手把手带,老员工会review新员工的代码,同
时查看原来老的代码中的问题(粗看),然后维护一份BUG和FAQ,只要可能是问题的都记录在案,包括编程习惯,设计,模型缺陷,新的想法之类。后面新
进员工进来接触到相关领域就把相关的FAQ先通读一遍,让他知道他接触到的东西很有可能就是错的,带着怀疑开始他的工作(的确也维护掉了老程序代码中的
一堆东西,呵呵学习中重构)。诶,不过没有专人来维护这样的文档,到后面这个文档本身也变成了一个问题。。目前逐步写简单的维护系统和定义文档规范
中。。

扯开了,楼主的文章想到几句话:
1.活到老学到老。
2.带着发展的眼光看问题。
3.权威的不一定是正确的。

另外,随着tp的不断进步和完善,应该会越来越好的扮演好一个引导者的角色吧。希望各位大侠多点耐心分析解答问题。

li li

unread,
Dec 9, 2008, 8:48:03 PM12/9/08
to pon...@googlegroups.com


2008/12/10 王宁 <nwan...@gmail.com>
同意!
另外对莫老师说一下:分三层在逻辑上比较清晰。UIL只管显示,BLL只管业务逻辑,DAL只管数据访问,每一层都具有一定的责任。这也是跟面向对象的三个原则:"对象就是责任"、"针对变化封装之"、"高内聚,低耦合"相符合的。BLL的确是封装,但不仅仅是简单的封装吧,它通常要把DAL提供的接口组合起来并处理一些异常。举个例子,比如DAL提供了Insert、Delete、Query、Update四个接口,现在BLL有个取钱的接口Withdraw里面组合了Query、Update这两个接口并返回一个UIL需要的结果,则UIL直接调用这个接口就行了。当业务逻辑出现变化,比如在Query和Update中间要加个Insert,UIL不用改变任何代码。当然可以执意把这些代码写入UIL,甚至所有代码都写入UIL,这都行,无非就是UIL里头写个BL类,DA类什么的也都能够符合那些原则,问题是如果项目大了以后这样就很痛苦了。

莫华枫

unread,
Dec 9, 2008, 9:49:09 PM12/9/08
to pon...@googlegroups.com


2008/12/10 nemo <moremo...@gmail.com>

不知道楼主是在什么样的阶段真正领悟到"我能想到的唯一方法就是,趁自己工作前,或者技术定型之前,打好基础,开拓眼界。不要迷信案例,带着怀疑 的眼
光和基本原则来看待他们"。我是在工作了蛮长一段时间后。经历了从X86平台到嵌入式平台。MS C++到linux下的C/C++开发以后。
很难说具体什么时候,这些想法是逐渐形成的。当然也是看到了很多程序员不合理的行为开始的。另一方面,我高中时代所参加的创造比赛对我的思维开拓有着很大的帮助,基本上使我养成了凡事疑三分的习惯。


这个世界变化太快,特别是计算机行业,对于普通程序员大众(包括我),在很多时候是在用别人写的库,模型等等。除了一些自己真正关心的领域,其他的好像
都是不求甚解。恩拿来用,大概了解下背景知识。有时候其实这些库啊模型啊或许已经不十分适合现在实际情况。要想扩展出去深入的了解一下。却发现又是一个
宽广的天地。。时间和精力啊。。
这个我觉得是"万变不离其宗"。很多问题最终还是能够归结到很多基础的思想、理论、方法和技术。


我们也积累了一大堆老的存在各种问题的代码,不过采取了一个不是办法的办法。目前基本上新进来的人都是手把手带,老员工会review新员工的代码,同
时查看原来老的代码中的问题(粗看),然后维护一份BUG和FAQ,只要可能是问题的都记录在案,包括编程习惯,设计,模型缺陷,新的想法之类。后面新
进员工进来接触到相关领域就把相关的FAQ先通读一遍,让他知道他接触到的东西很有可能就是错的,带着怀疑开始他的工作(的确也维护掉了老程序代码中的
一堆东西,呵呵学习中重构)。诶,不过没有专人来维护这样的文档,到后面这个文档本身也变成了一个问题。。目前逐步写简单的维护系统和定义文档规范
中。。
师父带徒弟的方式在现代社会生产中毕竟不太合适,但是也没有办法。本该在高校中获得的能力却被推迟到了工作中。


扯开了,楼主的文章想到几句话:
1.活到老学到老。
2.带着发展的眼光看问题。
3.权威的不一定是正确的。

另外,随着tp的不断进步和完善,应该会越来越好的扮演好一个引导者的角色吧。希望各位大侠多点耐心分析解答问题。

莫华枫

unread,
Dec 9, 2008, 10:26:35 PM12/9/08
to pon...@googlegroups.com
2008/12/10 li li <popi...@gmail.com>

另外对莫老师说一下:分三层在逻辑上比较清晰。UIL只管显示,BLL只管业务逻辑,DAL只管数据访问,每一层都具有一定的责任。这也是跟面向对象的三个原则:"对象就是责任"、"针对变化封装之"、"高内聚,低耦合"相符合的。BLL的确是封装,但不仅仅是简单的封装吧,它通常要把DAL提供的接口组合起来并处理一些异常。举个例子,比如DAL提供了Insert、Delete、Query、Update四个接口,现在BLL有个取钱的接口Withdraw里面组合了Query、Update这两个接口并返回一个UIL需要的结果,则UIL直接调用这个接口就行了。当业务逻辑出现变化,比如在Query和Update中间要加个Insert,UIL不用改变任何代码。当然可以执意把这些代码写入UIL,甚至所有代码都写入UIL,这都行,无非就是UIL里头写个BL类,DA类什么的也都能够符合那些原则,问题是如果项目大了以后这样就很痛苦了。
说的对。这些原本就是多层模型的愿景。但是,在实际中却出现了BLL中没东西可放的情况。这又很多原因。主要是由于BLL的部分功能被抽离到其他两层。为什么会这样?这就涉及到分层的本质。如果Withdraw接口被若干UI组件使用,那么这种抽象是有益的。因为Withdraw有所变化时,只需修改一个地方即可。但是,在实际情况中,我们发现,这种公用的情况不多。被一个UI模块使用的业务逻辑要多于被多个模块使用的业务逻辑。这样,单独维护这些业务逻辑就没什么意义,但增加了维护的难度。
而且,有些被多个模块使用的业务逻辑在开发,甚至使用的过程中发生变化,而无法被多个模块使用。麻烦的是,这些分化出来的业务逻辑在某些方面又有共同的地方,这样的重构给开发工作带来很大的麻烦。所以,很多程序员宁愿把业务逻辑放在ui里,一旦出现公用的情况,在把它抽离出来。为了方便,他们也就随手把这些公共模块留在了ui中。
另一方面,三层模型有效地发挥作用,需要有相对固定的需求,特别是业务对象的逻辑关系尽量少变动。而上层的变化尽量局限在业务过程的变化。但是,在我经历国的项目中,变化最多的反而是业务对象的结构和逻辑关系。用户最初的需求往往极不成熟,只有在开发到一定程度,甚至上线之后,才会得到真正有用的需求。而此时的需求同原始需求可能差着十万八千里,基本上都是根本性的变化。反倒是Withdraw这样的变化很少出现。这样的情况使得三层模型根本无用武之地,反而成了累赘。
此外,在实际项目中,ui同业务逻辑之间的关系比想象的要密切得多。两者之间任何一个的变化,往往会引发另一个的变化。比如,在一个录入界面中,业务逻辑需要增加一个业务对象的属性赋值,那么多半会要求用户给主相应的直或对象,这必然会引发ui的变化。同时,也会要求相应的业务逻辑接口增加参数,以容纳新的数据。正因为如此,实际情况中,程序员更多地倾向于将业务逻辑安置在ui中,便于维护。
面向对象有其原则,而编程亦有其原则。当它们两者之间发生冲突,就像那个三层模型那样,应当选择哪一个呢?

btw:商量一下哈,不要叫我"老师"行不:)。让我很紧张,不自在:)。在一个group里的都是兄弟姐妹,顶多叔伯姨舅:D。
我声明一下哈,大家叫我姓名,名字,老莫,都可以。如果想找个新鲜的,大学里别人叫我"长脚"(longshanks),我也很喜欢的:) 。还有家里的小名...。呃,小名就算了。:D
拜托,多谢了。:)

pongba

unread,
Dec 9, 2008, 10:46:33 PM12/9/08
to pon...@googlegroups.com
大赞,说的真好!:) 回头把《The Art of PM》列入待读!

2008/12/10 王宁 <nwan...@gmail.com>

li li

unread,
Dec 9, 2008, 11:10:51 PM12/9/08
to pon...@googlegroups.com


2008/12/10 莫华枫 <longsh...@gmail.com>


另一方面,三层模型有效地发挥作用,需要有相对固定的需求,特别是业务对象的逻辑关系尽量少变动。而上层的变化尽量局限在业务过程的变化。但是,在我经历国的项目中,变化最多的反而是业务对象的结构和逻辑关系。用户最初的需求往往极不成熟,只有在开发到一定程度,甚至上线之后,才会得到真正有用的需求。而此时的需求同原始需求可能差着十万八千里,基本上都是根本性的变化。反倒是Withdraw这样的变化很少出现。这样的情况使得三层模型根本无用武之地,反而成了累赘。
此外,在实际项目中,ui同业务逻辑之间的关系比想象的要密切得多。两者之间任何一个的变化,往往会引发另一个的变化。比如,在一个录入界面中,业务逻辑需要增加一个业务对象的属性赋值,那么多半会要求用户给主相应的直或对象,这必然会引发ui的变化。同时,也会要求相应的业务逻辑接口增加参数,以容纳新的数据。正因为如此,实际情况中,程序员更多地倾向于将业务逻辑安置在ui中,便于维护。
面向对象有其原则,而编程亦有其原则。当它们两者之间发生冲突,就像那个三层模型那样,应当选择哪一个呢?

btw:商量一下哈,不要叫我"老师"行不:)。让我很紧张,不自在:)。在一个group里的都是兄弟姐妹,顶多叔伯姨舅:D。
我声明一下哈,大家叫我姓名,名字,老莫,都可以。如果想找个新鲜的,大学里别人叫我"长脚"(longshanks),我也很喜欢的:) 。还有家里的小名...。呃,小名就算了。:D
拜托,多谢了。:)
我没有从事过很大的项目,所以不能够理解这段话所描述的困难。但我明白,变化是永恒的。你不可能不考虑需求的变化,但考虑变化会使系统产生很大的冗余,复杂性也随之提高,这样BUG也会增多,这确实是考验软件管理者的设计的。令人惊奇的是UNIX的设计!当初Ken只是想"玩"一把,完全是处于自己兴趣,并没有考虑很多变化,没有作太多设计,而事实证明UNIX是多么成功,无独有偶,LINUX也是,刚开始的版本只是能够运行而已,需要什么功能都是现加入,LINUX依然是成功的。而具有讽刺意味的是UNIX的前辈MULTICS,作了那么多充足的估计,考虑到了无数的变化,最后依然因为它的复杂性而失败。然而,事实是现在大多数成功的软件都是考虑周全的结果。令人困惑的是,在复杂性和简洁之间如何抉择?

ps:我们这流行:昵称=名字的最后一个字 + "子",比如,徐杰被叫做"杰子",那么你应该被叫做"枫子"喽:D

Adam Jiang

unread,
Dec 9, 2008, 11:42:51 PM12/9/08
to pon...@googlegroups.com
2008/12/10 li li <popi...@gmail.com>


2008/12/10 莫华枫 <longsh...@gmail.com>


另一方面,三层模型有效地发挥作用,需要有相对固定的需求,特别是业务对象的逻辑关系尽量少变动。而上层的变化尽量局限在业务过程的变化。但是,在我经历国的项目中,变化最多的反而是业务对象的结构和逻辑关系。用户最初的需求往往极不成熟,只有在开发到一定程度,甚至上线之后,才会得到真正有用的需求。而此时的需求同原始需求可能差着十万八千里,基本上都是根本性的变化。反倒是Withdraw这样的变化很少出现。这样的情况使得三层模型根本无用武之地,反而成了累赘。
此外,在实际项目中,ui同业务逻辑之间的关系比想象的要密切得多。两者之间任何一个的变化,往往会引发另一个的变化。比如,在一个录入界面中,业务逻辑需要增加一个业务对象的属性赋值,那么多半会要求用户给主相应的直或对象,这必然会引发ui的变化。同时,也会要求相应的业务逻辑接口增加参数,以容纳新的数据。正因为如此,实际情况中,程序员更多地倾向于将业务逻辑安置在ui中,便于维护。
面向对象有其原则,而编程亦有其原则。当它们两者之间发生冲突,就像那个三层模型那样,应当选择哪一个呢?

btw:商量一下哈,不要叫我"老师"行不:)。让我很紧张,不自在:)。在一个group里的都是兄弟姐妹,顶多叔伯姨舅:D。
我声明一下哈,大家叫我姓名,名字,老莫,都可以。如果想找个新鲜的,大学里别人叫我"长脚"(longshanks),我也很喜欢的:) 。还有家里的小名...。呃,小名就算了。:D
拜托,多谢了。:)


Hello, longshanks.

还是这样称呼吧。:) 我觉得你谈了一个在软件设计中非常重要的问题。"分"和"合"也就是利弊权衡的事情,但是从整个软件生命周期来看,这两种决定就像钟摆一样,颠过来倒过去。但我觉得最重要的事情还是需求分析和何时重构。也有人说需求分析是靠不住的,因为没人知道自己想要什么。但假设一个极端状态,也就是需求非常清晰没有疏漏的情况下,也并不存在一个标尺能够衡量出究竟分到什么程度或者合成什么样子才是合适。建模是桥梁,一个良好的设计应该不仅仅是对需求现实的直接描述,而应该是一个加入一种灵活的技巧和设计,进行间接描述,尽可能的把这种分和合的变化程度降低,保证代码有比较高的重用率。MVC之所以深入人心也是因为他解决了复杂软件设计的一个大问题。但软件设计还是应该灵活一些,把业务逻辑写进表示曾也表示罪大恶极,我是作embedded的,我常干这种事情。我们有时后需要妥协:) 看过google关于android平台的介绍视频,演讲的兄弟说android是一个real software stack,把real重重读了两边,也许就是在强调android的结构良好。

你也谈到一种三曾模型失效的情况。但我觉得这个情况发生的前提是软件没有进行很好的迭代开发。如果用原型技术深刻挖掘用户需求的话,需求变化是可以的到适度的控制的。当然这只是我在我的工作中的感受,每个人遇到的情况是不同的,不能一概而论。另外,设计模式描述了一套被很多成功的系统证实的一种灵活的设计方法,但它的局限在于,还是需要软件设计人员审时度势的运用才能发挥效果。所以,三曾模型也不是放之四海皆准的。要知道这个诞生在SmartTalk里的东西,其实最初只是用来抽象GUI程序的开发。

后面有兄弟回信提到UNIX和Linux,也提出复杂性和简洁之间如何抉择的事情。但是,我认为,应该把选择的重点放在"灵活"上面。你需要一种结构或者方法,保证你的系统能够灵活的变化。看看Linux内核的API和ABI就知道了,开发者声称,事实上驱动程序的开发者需要的不是一个stable interface,而想要的是Linux内核在版本升级后也能够无痛的使用或者升级旧的驱动。这就抓住了问题的本质,于是他们采取的方法是,谁改变了内核的interface,谁就必须负责将所有的/driver目录下的开源驱动程序全部更新。当然他不可能一个人做完所有变更,所谓的负责就是他必须清晰的告诉驱动程序开发者因该怎样更改。That's all.
 
我没有从事过很大的项目,所以不能够理解这段话所描述的困难。但我明白,变化是永恒的。你不可能不考虑需求的变化,但考虑变化会使系统产生很大的冗余,复杂性也随之提高,这样BUG也会增多,这确实是考验软件管理者的设计的。令人惊奇的是UNIX的设计!当初Ken只是想"玩"一把,完全是处于自己兴趣,并没有考虑很多变化,没有作太多设计,而事实证明UNIX是多么成功,无独有偶,LINUX也是,刚开始的版本只是能够运行而已,需要什么功能都是现加入,LINUX依然是成功的。而具有讽刺意味的是UNIX的前辈MULTICS,作了那么多充足的估计,考虑到了无数的变化,最后依然因为它的复杂性而失败。然而,事实是现在大多数成功的软件都是考虑周全的结果。令人困惑的是,在复杂性和简洁之间如何抉择?

ps:我们这流行:昵称=名字的最后一个字 + "子",比如,徐杰被叫做"杰子",那么你应该被叫做"枫子"喽:D



--
Adam Jiang
------------------------------------------
e-mail:jiang...@gmail.com

Kenny Yuan

unread,
Dec 10, 2008, 12:08:04 AM12/10/08
to pon...@googlegroups.com
考虑复杂性,或者简洁……多设计,或者少设计……所有的这些其实都是事后的归因,有可能是必要条件,也有可能仅仅是相关

我们去C++某个工程,我们去JAVA某个工程,我们去PATTERN,我们GP,我们FP,我们DRY,我们KISS,我们UML,我们SCRUM,我们不管做什么事情,项目还是有可能成功或者失败。而且,和传说中的不一样的是:建筑业同样还是会失败,包括项目延期,项止超支,项目中止,项目不合需求,项目设计失误,只是失败的概率较小罢了(比如悉尼歌剧院符合前面说的五条中的四条,还有某个著名的修了几十年的大桥)

如果有人能够找到软件成功的充分条件,找到那个传说中的银弹,你的问题才可能得到回答。

P.S. 我们听从许多技术/管理上的劝告之后去做一个项目,除去在技术/方法上带来的改进之外,还带来了自信,以及必胜的信念。这与药品实验中的安慰剂效应类似。作为管理者,我们要去尝试利用这个效应,就好比医生会利用安慰剂效应一样。需要排除安慰剂效应的是药品/治疗方式的临床实验,对应于软件工程中的方法学研究。

以上仅为个人看法。用到的类比也可能不很贴切,仅作启发之用。


2008/12/10 li li <popi...@gmail.com>

令人惊奇的是UNIX的设计!当初Ken只是想"玩"一把,完全是处于自己兴趣,并没有考虑很多变化,没有作太多设计,而事实证明UNIX是多么成功,无独有偶,LINUX也是,刚开始的版本只是能够运行而已,需要什么功能都是现加入,LINUX依然是成功的。而具有讽刺意味的是UNIX的前辈MULTICS,作了那么多充足的估计,考虑到了无数的变化,最后依然因为它的复杂性而失败。然而,事实是现在大多数成功的软件都是考虑周全的结果。令人困惑的是,在复杂性和简洁之间如何抉择?



--
Kenny Yuan
C++, UI, LISP, MMA, Psychology and Automobile.
BLOG: CS巴别塔(Computer Science Babel)
URL: http://blog.csdn.net/yuankaining/


战斗暴龙

unread,
Dec 10, 2008, 12:29:20 AM12/10/08
to pon...@googlegroups.com
 这个话题,我也可以说一些,虽然我现在还是一个大三学生。

大二其间,曾经自发的组织了一个0起步团队(成员是我认为的比较有编程潜力的同学),出于对分工的考察,我布置了一个简单的任务"登陆系统",所有成员独立完成,技术、思路不限,时间一周(考虑到还有很多其他的事,比如教学计划之类的)。由于这个东西太"简单",而且虽然语言环境不同,但是这类小模块学校也布置过很多,其中就有一部分人不屑于做,交上来的一部分,没有一个我满意的。主要有两类情况,一类是把以前做过的拿出来修修改改,总之跟他第一次做时的思路基本就没有不同。还有一类是生搬硬套的,这部分人是在我的建议下开始自学设计模式的。

这暴露了另外一个问题,很多大学生,每学到一个新的东西,就都想着把它塞到自己做的东西里,却不知道这样只会徒增麻烦,这到不是说绝对不应该这么做,实际上,不仅是自己做的东西,就是平时正常的交流中,有人(这部分通常就是比较好学的,接触面比较广的人)也是一口一个术语,连他自己都不理解的术语。根本原因就在于没有足够的实践基础,正如上面有朋友所说"教程里的例子没什么东西可以用来填充框架或模式,但是又不能没有",这样最直接的负面影响就是,别人给他解释他理解不了,他自己表达又表达不清,于是学习进度很慢。我就不懂那些术语,但是我设计的比他好,因为我有项目经验。无论对于什么项目,都应当是"技术服务需求",而不是"需求跟着技术走",初学者就喜欢因为学了新技术而盲目改变原有的需求。

教育的责任也不少,想我们这样的跟所谓的"教育联盟"合作办学的以计算机应用层为目标的学校也不在少数,说是教育联盟,实际上我找不到它们跟培训机构的本质区别。一学期甚至半学期就换一门语言,然后所教的内容有没有多少明显的改变,学生基础还未打扎实,却看到教学计划上一个又一个应用框架做为下阶段的目标。如果能够一直深入下去,那到好了,可要命的是还是在原先那种弱智的课程设计的基础上套框架。除了使原先问题的"裹脚布"越厚越臭外,学生的思维没有任何提高。有时侯我在想,既然培训机构确实存在,而且也确实有太多的学生毕业之后不做原专业的事,那为什么学校还要跟"教育联盟"合作,为什么不直接在教学上就按需开课?

大学生接触的比较多的项目也就是那些三层架构的项目,并且也多为一些数据结构上的差异,wrapper必不可少,在这种情况下,我觉得如果一定要wrap,不妨结合语言本身的特性,找个比较统一的做法,比如反射、动态代理之类的东西。复杂一点的,更改架构思路,总之对于遵循同一模式的东西,尽可能的让它自适应,"变化"才是最高级的需求,从来就没有能一劳永逸的方案。以前一直以为面向对象很牛,什么项目首先考虑对象,后来接触的东西多了,才知道任何一种思维方式都不可能取代或部分取代其他的,选择什么取决于问题领域和与之相关的外围环境。

PS:我比较一根筋,几乎就不喜欢参考案例,懒得很。要什么例子没有的就自己写,比如,嫌烦不学hibernate,然后自己根据它的原理来写数据访问层,恰好够用就行。并且成功的应用在了那次的团队项目里(不是教学计划中的项目)。我不知道这样的学习方式有什么弊端,也没法跟人家做比较,在这样的教育环境下。大学生包括我,其实都很"坐井观天"。

crazyender

unread,
Dec 10, 2008, 12:59:37 AM12/10/08
to pon...@googlegroups.com

借你这段说几句。我上了四年大学,当时学的最开心的课程是微积分,计算理论,数据结构,操作系统理论等等偏理论的课,而那些实践性比较强的比如c++java啥的,基本上就是在脑子里走个过场。目前工作所需的--恕我直言--“小伎俩”,都是在工作后学会的。我认为大学的目的在于培养学生的独立思考能力,训练他们如何成为一个合格的“现代公民”,虽然社会生产实践能力也是现代公民应有素质之一,但绝不会是全部。反观目前的高校,学生都拼命的想找个实习的机会,尽早的成为熟练工以便毕业能找个好工作;老师则拼命的接各类项目以便,你知道,赚更多的钱。他们统统对人类积累的上百年的智慧视而不见。我不知道这是一个转型期社会的“特色”,还是中国人又缺乏了某类精神特质。

我回这个帖子是因为看到暴龙同学在抱怨学校,也想跟着抱怨一下,没切主贴的题,见谅。

 


莫华枫

unread,
Dec 10, 2008, 2:38:13 AM12/10/08
to pon...@googlegroups.com
2008/12/10 li li <popi...@gmail.com>
我没有从事过很大的项目,所以不能够理解这段话所描述的困难。但我明白,变化是永恒的。你不可能不考虑需求的变化,但考虑变化会使系统产生很大的冗余,复杂性也随之提高,这样BUG也会增多,这确实是考验软件管理者的设计的。令人惊奇的是UNIX的设计!当初Ken只是想"玩"一把,完全是处于自己兴趣,并没有考虑很多变化,没有作太多设计,而事实证明UNIX是多么成功,无独有偶,LINUX也是,刚开始的版本只是能够运行而已,需要什么功能都是现加入,LINUX依然是成功的。而具有讽刺意味的是UNIX的前辈MULTICS,作了那么多充足的估计,考虑到了无数的变化,最后依然因为它的复杂性而失败。然而,事实是现在大多数成功的软件都是考虑周全的结果。令人困惑的是,在复杂性和简洁之间如何抉择?
多数成功的系统都经历过重构。一旦重构,实际上就获得了稳定的需求,因为前面的版本已经对需求有过全面的探索。说起来,UNIX也应该算是重构的系统,对MULTICS的重构。
而且,很多成功的软件都是基础的系统,unix是操作系统,Firefox是浏览器等等。基础系统往往有稳定的需求,并且可以迭代。但是三层模型所面对的是应用系统和业务系统。这些系统的需求变化是出了名的,很少有稳定的时候。我们有个项目,已经做了5年了,已经在维护阶段了,还会不时地有新的需求。业务系统通常也很难说是成功还是失败,一般只要上线,能跑起来,然后再慢慢修补。即便是sap这样成熟了近30年的系统,实施也是个长期过程。
还有一点,UNIX的成功看似神奇,但蕴含着必然。早期设计者们遵循着最质朴、最根本的软件设计理念,但它们却是最本质的。而后来者继续秉承他们的思想发扬光大。这正是现在很多程序员所应当学习的。UNIX设计者们并未炫耀某种先进的模式或者技术,他们踏踏实实地、一步一趋地遵循着基本的设计原则(KISS、DRY、Simple Interface、Small is beautiful...)。而所使用的技术甚至是土得掉渣的。
如果面临着复杂和简洁的选择,那毋庸置疑,选择简洁。
另外,从UNIX想到,现在面向对象化三层模型所秉承的思想是writing everything in one。而UNIX则更加提倡Mixing languages。我们是否可以从另一个角度想想,三层模型是否一定要writing everything in one吗?是否一定要类吗?是否一定要oop吗?不用oop,还有其他方式吗?其它方式是更好还是更差呢?...
思想的进步,起步于怀疑。


ps:我们这流行:昵称=名字的最后一个字 + "子",比如,徐杰被叫做"杰子",那么你应该被叫做"枫子"喽:D
:D

莫华枫

unread,
Dec 10, 2008, 3:00:56 AM12/10/08
to pon...@googlegroups.com
再说一个刚想起来的故事,是听书听来的。是三国演义,评话(就是苏州人说的"大书",上海人都喜欢听:))。
说是诸葛亮出祁山用木牛运粮草。司马懿得知,前去截粮。蜀军一见魏军就跑,司马懿缴获了大量粮草和木牛。回去后找来最好的工匠,要他们仿造木牛。那些工匠非常认真,每个零件每个零件地仿,仿得一模一样。甚至有的零件上有个木节,他们也找了一块有同样木节的木料加工。
几个月后,每个木牛都仿制了一个。司马懿也用这些木牛运粮。可是走到一个地方突然这些木牛动不了了,一个个戳在那里。正当蜀军疑惑之时,蜀军杀来,打跑了魏军,夺回了粮草和木牛,而且还多一倍。木牛不动怎么办呢?蜀军士兵把木牛的舌头拽了一下,木牛就又走了。
原来诸葛亮在木牛里按了机关,走到一定地方就会停下不动。司马懿的工匠们由于不懂机关的原理,按部就班地连同机关一起复制过来。结果不单损失了粮草,还帮着诸葛亮造了一批木牛。

li li

unread,
Dec 10, 2008, 3:49:51 AM12/10/08
to pon...@googlegroups.com


2008/12/10 莫华枫 <longsh...@gmail.com>

早期设计者们遵循着最质朴、最根本的软件设计理念,但它们却是最本质的。而后来者继续秉承他们的思想发扬光大。这正是现在很多程序员所应当学习的。UNIX设计者们并未炫耀某种先进的模式或者技术,他们踏踏实实地、一步一趋地遵循着基本的设计原则(KISS、DRY、Simple Interface、Small is beautiful...)。而所使用的技术甚至是土得掉渣的。

这是我崇尚的。

up duan

unread,
Dec 10, 2008, 4:09:38 AM12/10/08
to pon...@googlegroups.com
Koenig说过:库设计就是语言设计,语言设计就是库设计[举例说明:我们其实没有必要区分sin函数是库还是语言特征,STL也同样如此]。他们是牛人,做到这一点相对容易,但对于我们来说,要想实现一个跟语言要素一样的库实在是困难之极。
所以我们一般就退而求其次用库来实现应用框架,应用框架比语言要素灵活性要差一些,因此可重用的几率要小一些,当然,效益就要差一些。
我们一般并不知望一个语言能完美覆盖所有的领域,即便是非常强悍的通用目的语言,在特定领域里至多也只能做到第二。更何况比语言灵活性更差一级别的框架呢?所以,不要指望一个框架能够覆盖所有的领域。不能把所有的问题都强制塞入一个特定的框架之中。
对于流行的层次式框架,如果这个层次是可协调和可适配的,那么,这个框架其实是一个指导思路,并隐隐跟"软件工程中的任何问题都可以通过一个间接层来解决"这个至理名言一样正确而无用。如果特定为用户交互层、业务逻辑层、数据访问层,那么难免不适应于特定的领域,过于复杂的领域或者过于简单的领域这个分层模式都不是很适应。

smzlkimi

unread,
Dec 10, 2008, 4:11:28 AM12/10/08
to pon...@googlegroups.com
应该是参照的MS的Pet Shop吧,Pet Shop数据访问层中并没有使用ORM,所以数据访问层很庞大,把数据库相关操作都进行了封装,在BLL,很多操作都是简单的存取操作,所以直接调用了DAL的方法,感觉反而代码量不多,可能是因为PET SHOP只是一个案例而已。
个人愚见: 觉得虽然BLL现在只是一些wrapper,但是考虑到以后项目的业务逻辑可能不断复杂起来,加上这些wrapper也未尝不可。
2008/12/9 莫华枫 longsh...@gmail.com
这里根本的问题是,如果DAL类的接口和BLL一样,就表明有业务相关的内容进入DAL了。
我觉得也有可能是系统的BLL层实在太简单,仅仅是数据的存取等,这样就直接调用下层了。
 
我也还是大学生,我觉得出现这种情况是我们接触的东西还太少,可能一共就了解那么几个架构,然后不管在哪,都直接往上套了。

yq chen

unread,
Dec 10, 2008, 6:15:12 AM12/10/08
to pon...@googlegroups.com
      所谓的考虑以后项目的业务逻辑可能不断复杂起来,个人感觉就是一个错误,是用臆想代替了实际的需求的可能性。
     人不可能预测所有的复杂性,也不能在一个代码中或者一个业务应用框架中解决所有的复杂性,这样做只能是徒劳无益的,抱着这样的目的去做项目也必然会失败的。
     在一个项目之前,一般人都不一定能够真正理解项目中需要什么,就算是分析、设计,如果经验不是十分丰富的话,也会走偏了到过度设计或者简单考虑问题的一端。
     其实能做的事情也不是没有,就是尽量保持设计和实现上的结构简单性。就算将来需求发生了变化,也能够迅速的适应需求的变化,所谓以不变应万变也。当然做到这一点也是十分困难的。
     成功的软件,感觉都是开始的目标和野心不是很大的东西,这样最初实现的版本实际上就是一个易于把握的原型了。后续版本的开发都是通过对这个原型版本的重构的过程,也是持续不断对业务和需求理解的一个过程。这样东西成功的案例有很多。
     因为简单,所以更易于理解和把握,因而更容易成功。


2008/12/10 smzlkimi <smzl...@gmail.com>

yq chen

unread,
Dec 10, 2008, 6:20:57 AM12/10/08
to pon...@googlegroups.com
结合到莫兄的例子来看,这个就是一个典型的过度设计,而且是一个很蹩脚的设计。如果一个模块只是把另一个模块简单的重复了一遍,而其目的只是为了所谓以后可能的扩展,那么这个东西多半是不靠谱的。反正就是简单的一层包装,就算到时候重写也不见得难到天上去,真正需要的时候再去扩展也是不迟的。

2008/12/10 yq chen <mephist...@gmail.com>

莫华枫

unread,
Dec 10, 2008, 6:32:17 AM12/10/08
to pon...@googlegroups.com
说的在理。
我见到的这种三层模型开发的软件,直到三四年后,项目已经完成,钱已经到帐,并且花完。BLL依然是一组空空的wrapper,没有任何扩展。

2008/12/10 yq chen <mephist...@gmail.com>

cnzhangzhen

unread,
Dec 10, 2008, 8:40:39 AM12/10/08
to TopLanguage
我不觉得这个师傅有多么蹩脚,至少教会了徒弟学会尽可能将逻辑分开--没理解的徒弟学了个形式,那也不坏啊。

多了一层没有意义的wrapper, 那又怎么样呢?真正影响到了什么呢?performance? 得了吧,还有更多的地方值得tuning.

以前读过印度啊三的代码(有嵌入式的,也有企业软件的),大都是这种路数,我觉得反而是"规范",变得亲切易读了呢。

代码工业化的今天,何必呢。


On 12月10日, 上午11时26分, "莫华枫" <longshank...@gmail.com> wrote:
> 2008/12/10 li li <popil1...@gmail.com>
>
>
>
> > 另外对莫老师说一下:分三层在逻辑上比较清晰。UIL只管显示,BLL只管业务逻辑,DAL只管数据访问,每一层都具有一定的责任。这也是跟面向对象的三个原则-:"对象就是责任"、"针对变化封装之"、"高内聚,低耦合"相符合的。BLL的确是封装,但不仅仅是简单的封装吧,它通常要把DAL提供的接口组合起来并处理-一些异常。举个例子,比如DAL提供了Insert、Delete、Query、Update四个接口,现在BLL有个取钱的接口Withdraw里面组合了Q-uery、Update这两个接口并返回一个UIL需要的结果,则UIL直接调用这个接口就行了。当业务逻辑出现变化,比如在Query和Update中间要加-个Insert,UIL不用改变任何代码。当然可以执意把这些代码写入UIL,甚至所有代码都写入UIL,这都行,无非就是UIL里头写个BL类,DA类什么的-也都能够符合那些原则,问题是如果项目大了以后这样就很痛苦了。
>
> 说的对。这些原本就是多层模型的愿景。但是,在实际中却出现了BLL中没东西可放的情况。这又很多原因。主要是由于BLL的部分功能被抽离到其他两层。为什么会-这样?这就涉及到分层的本质。如果Withdraw接口被若干UI组件使用,那么这种抽象是有益的。因为Withdraw有所变化时,只需修改一个地方即可。但-是,在实际情况中,我们发现,这种公用的情况不多。被一个UI模块使用的业务逻辑要多于被多个模块使用的业务逻辑。这样,单独维护这些业务逻辑就没什么意义,但-增加了维护的难度。
> 而且,有些被多个模块使用的业务逻辑在开发,甚至使用的过程中发生变化,而无法被多个模块使用。麻烦的是,这些分化出来的业务逻辑在某些方面又有共同的地方,这-样的重构给开发工作带来很大的麻烦。所以,很多程序员宁愿把业务逻辑放在ui里,一旦出现公用的情况,在把它抽离出来。为了方便,他们也就随手把这些公共模块留-在了ui中。
> 另一方面,三层模型有效地发挥作用,需要有相对固定的需求,特别是业务对象的逻辑关系尽量少变动。而上层的变化尽量局限在业务过程的变化。但是,在我经历国的项-目中,变化最多的反而是业务对象的结构和逻辑关系。用户最初的需求往往极不成熟,只有在开发到一定程度,甚至上线之后,才会得到真正有用的需求。而此时的需求同-原始需求可能差着十万八千里,基本上都是根本性的变化。反倒是Withdraw这样的变化很少出现。这样的情况使得三层模型根本无用武之地,反而成了累赘。
> 此外,在实际项目中,ui同业务逻辑之间的关系比想象的要密切得多。两者之间任何一个的变化,往往会引发另一个的变化。比如,在一个录入界面中,业务逻辑需要增-加一个业务对象的属性赋值,那么多半会要求用户给主相应的直或对象,这必然会引发ui的变化。同时,也会要求相应的业务逻辑接口增加参数,以容纳新的数据。正因-为如此,实际情况中,程序员更多地倾向于将业务逻辑安置在ui中,便于维护。


> 面向对象有其原则,而编程亦有其原则。当它们两者之间发生冲突,就像那个三层模型那样,应当选择哪一个呢?
>
> btw:商量一下哈,不要叫我"老师"行不:)。让我很紧张,不自在:)。在一个group里的都是兄弟姐妹,顶多叔伯姨舅:D。
> 我声明一下哈,大家叫我姓名,名字,老莫,都可以。如果想找个新鲜的,大学里别人叫我"长脚"(longshanks),我也很喜欢的:)
> 。还有家里的小名...。呃,小名就算了。:D
> 拜托,多谢了。:)
>
> --
> 反者道之动,弱者道之用
> m...@seaskysh.com

> longshank...@gmail.comhttp://blog.csdn.net/longshanks/

Googol Lee

unread,
Dec 10, 2008, 8:58:29 AM12/10/08
to pon...@googlegroups.com
这个师傅蹩脚在没有教会徒弟"为什么",而只是是什么。

问题不在于多了一层没有意义的wrapper,而是在于不知道为什么要多这层wrapper。

老莫举的实际中的例子可能不是很合适,比如最初设计这一层的时候可能考虑到后面的数据库替换的影响要尽可能的小,但在实际工作中却从没发生过数据库变化的情况。一般新增逻辑似乎更倾向于在数据库中加新表,而不是重构已有数据库,可能这也是导致逻辑层wrapper变化不大的原因吧。

2008/12/10 cnzhangzhen <cn.zha...@gmail.com>



--
新的理论从少数人的主张到一统天下,并不是因为这个理论说服了别人抛弃旧观点,而是因为一代人的逝去。

My blog: http://googollee.blog.163.com

xiaochang ma

unread,
Dec 10, 2008, 9:04:11 AM12/10/08
to pon...@googlegroups.com

说起来惭愧,当年我本科的时候对软件构架方面还不是很清楚,做毕业设计的时候就是将业务逻辑和UI混在一块的。由于是一个实际的工程项目,老板的需求也不是很明确,导致界面不停的修改。每修改一次界面,我的业务代码就得大改一次,弄得很是狼狈。在后来接触到软件构架的知识后,才发现如果用分层的结构就会很容易地解决这个问题,即使他的界面再改,我的DAL层提供的接口是不变的,直接调用就可以了,用不着大的改动了。
分层是必须的,但是分层的目的和怎么分就得靠读书和实践的积累来慢慢体会了。

2008/12/9 莫华枫 <longsh...@gmail.com>

我见到过一种三层模型,分为UI、BLL、DAL。此外还有一组Model类,用于封装实际的业务对象。这没什么特别的,多数三层模型都是这样。但是,这种三层模型有个特殊的地方,对于每一个描述业务对象的Model类,在BLL里基本上都有一个类对应,实时查询、检索等等操作。差不多就是个管理类吧。但如果深入到这些类内部,发现它们只是一组wrapper。其内部保有一个对象,该对象的类来自于DAL。而BLL中的这些类上的操作在DAL相对应的类上都有。BLL类实际只是在转发操作,而真正工作的,是DAL中的那一组类。

我觉得很奇怪,为什么要凭空锻造出这么一组毫无用处的类呢?得到的答案是:从MS的案例中学来的。诧异之余,我未免心中起疑,MS再差劲,也不至于直眉瞪眼地破坏DRY吧。肯定是这些家伙学艺不精,会错MS的意了。

不久前,公司来了一个应聘者,走的是.net路线,在外做过几个项目。我们出编程题考他,结果他做的程序的结构也是三层模型,居然和上面所说的结构一模一样,也是在BLL中放了一组无用的wrapper。关键是,此人和那个三层模型的创造者此前完全不可能相识。那么,只有两种可能。要么是他们以同样的方式错误理解了MS的案例;要么就是MS的案例本来就是这样的。

MS的案例究竟如何,我没有考证过,也无意去关心。我想到的是一个程序员如何开始学习编程。很多程序员喜欢从案例开始学习。从抄写代码,慢慢变为小修小改,然后逐步理解,最后开始单飞。但是,这里有个问题。程序员就像小鸟。小鸟会把第一眼看见的会动的东西当成妈妈,而程序员则会把最初学习的案例当成典范。

因而,程序员最初开始的编程学习就显得非常重要。一旦定型,以后很难改掉。一个程序员最初不应该从某些应用型的案例中开始学习。更多地应该把精力放在基本功上,像基本的编程原则、算法、范式等等。并且从一些小案例开始,比如写个小算法、小工具等等。此时的程序员还缺乏判断力,无法认清那些复杂案例中的问题和陷阱。上面提到的这种三层模型,我相信ms原意并非如此。但是,涉世不深的程序员很容易误解其中的用意,然后加以发挥。在没有正确引导的情况下,误入歧途。

可是,现实往往是残酷的。很多程序员并没有机会获得完善的培训。他们在大学里没有打好基础,来到工作单位就立刻投入开发工作,很多都是从接手和维护其他人的代码开始。企业希望以此快速地培养出程序员。但是这也往往使得那些代码中原本存在的缺陷被新手们充分吸收。老程序员往往无暇顾及那些新人。有时,甚至他们自身也未曾意识到存在的问题。这样,错误就一代接一代地传了下去。

指出了这些问题之后,我却不知道有什么好方法能够破解。我能想到的唯一方法就是,趁自己工作前,或者技术定型之前,打好基础,开拓眼界。不要迷信案例,带着怀疑的眼光和基本原则来看待他们。多问(不是在这里哈),多想,多google,多wiki,还有多上TopLanguage。:P


yq chen

unread,
Dec 10, 2008, 9:33:00 AM12/10/08
to pon...@googlegroups.com
这个地方的关键不是效率(像.NET这种在效率上只能是just so so的东西,不提也罢),而是重复。把一个东西重复写两遍,在两个地方。到时候一个地方改掉了,还要去改另一个地方。何必呢?代码的分层是必要的,是逻辑分层,业务分块,没有必要什么都弄个三层架构、四层架构的,然后每天都陪着去维护代码。

up duan

unread,
Dec 10, 2008, 10:39:52 AM12/10/08
to pon...@googlegroups.com
2008/12/10 cnzhangzhen <cn.zha...@gmail.com>
我不觉得这个师傅有多么蹩脚,至少教会了徒弟学会尽可能将逻辑分开--没理解的徒弟学了个形式,那也不坏啊。

关键是形式有时候有害啊。

多了一层没有意义的wrapper, 那又怎么样呢?真正影响到了什么呢?performance? 得了吧,还有更多的地方值得tuning.

性能可以不管,关键是可维护性和可修改性的问题,这是对DRY【或者叫做SPOT】原则的公然违背,维护性变得非常差。
 

以前读过印度啊三的代码(有嵌入式的,也有企业软件的),大都是这种路数,我觉得反而是"规范",变得亲切易读了呢。

代码工业化的今天,何必呢。

或许这在一定程度上让代码的可理解性增强了,但是我还是觉得这种方式的增强【通过集体降低理解能力】不要也罢。这样不利于解决较为另类的问题,而软件开发基本上总是"另类"的。

Linker

unread,
Dec 10, 2008, 12:15:33 PM12/10/08
to pon...@googlegroups.com
多样性存在于任何复杂的系统,应该也存在于软件开发。
这里的很多方法论的问题,其实归根到底就是对多样性估计不足。
虽然,我不看好类似软件蓝领的观点,但是这也确实是软件发展的多样性的一个方面。
换句话说,很多吃屎,我们吃饭才香。
如果大家都吃饭,反恐怕就不够吃了。
必然有很多人用不是最优的方法在做事。
学习的方法也是这样。


2008/12/10 up duan <fix...@gmail.com>



--
Regards,
Linker Lin
linker...@gmail.com

nemo

unread,
Dec 10, 2008, 7:52:03 PM12/10/08
to TopLanguage

On 12月10日, 下午3时38分, "莫华枫" <longshank...@gmail.com> wrote:
> 2008/12/10 li li <popil1...@gmail.com>
>

> 还有一点,UNIX的成功看似神奇,但蕴含着必然。早期设计者们遵循着最质朴、最根本的软件设计理念,但它们却是最本质的。而后来者继续秉承他们的思想发扬光大 。这正是现在很多程序员所应当学习的。UNIX设计者们并未炫耀某种先进的模式或者技术,他们踏踏实实地、一步一趋地遵循着基本的设计原则(KISS、DRY、 Simple
> Interface、Small is beautiful...)。而所使用的技术甚至是土得掉渣的。

大道至简的道理。

然则师傅带入门,修行还是靠自身。同样的一个东西,个人得到的领悟是不一样的。
不要陷入技术和权威的迷区就好。

程序员毕竟应该是一份创造性的职业,在积累了一定的经验以后,要鉴定的走创造的道路,而不只是单纯的模仿。

hoo volant

unread,
Dec 10, 2008, 10:15:15 PM12/10/08
to pon...@googlegroups.com
个人认为考虑多一些并不是什么坏处,但坏就坏在不区分"想和做"。分析的时候可以多想想,但设计的时候却应该趋向于简单,难就难在从复杂到简单的过程。

2008/12/10 yq chen <mephist...@gmail.com>

莫华枫

unread,
Dec 11, 2008, 3:26:59 AM12/11/08
to pon...@googlegroups.com
考虑未来业务的扩展,一定需要wrapper和空空的BLL吗?或者说,只有wrapper才能提供未来业务的扩展吗?
这里,我给出另一个途径。我们可以先去掉BLL,或者将DAL中与业务有关的内容移到BLL中。也可以理解为由UI直接访问DAL。这样,所有基本的业务对象的操作由UI直接从DAL处获得,比如业务对象的增删改查等等。然后,对于复杂的业务,比如需要组合多个基本业务操作的逻辑,在UI中实现。随着业务的复杂化,某些业务逻辑会由多个UI所共享,那么就将这些代码分离出来,形成一个模块,或者UI中的亚层。或许这个亚层也可以称为BLL(实际上它叫什么有什么差别吗?),但按照标准的分层理论,它够不成一个层次,因为他没有将上层和下层隔完全离开。不过,这对于实际应用没有什么影响。即便在那些标准的BLL中,也存在很多操作,如复杂的查询,需要绕开DAL直接访问数据库。因而这些三层模型也并非严格意义上的分层。
再有,这些wrapper的存在对代码修改的难度有多大的影响呢?让我们假设有一个业务逻辑A被UI调用,并且只有一个使用点。现在可以考察一下,当A发生变动时(暂时不考虑接口的变动,因为接口一变,UI通常都得改),究竟是A放在UI中修改起来麻烦呢,还是A放在单独的BLL中麻烦呢?实际上,两这是一样的。因为无论A在哪里,我只需修改一个地方,要么在UI里,要么在BLL里。
现来再来看一下,如果A在UI中的多个地方使用(代码直接嵌入UI),会怎么样。一旦A变动的话,如果放在UI里,需要修改多个地方的代码。而如果放在BLL中的话,只需修改一个地方。但是,如果我们在UI中将业务逻辑A分离到一个独立模块中的话,也可以只改动一个地方,而不需要修改UI本身。由此可见,这里的关键不是业务逻辑在哪里,而是业务逻辑是如何被抽象的。本质上,控制业务逻辑修改困难程度的,还是DRY原则。
所以说,并没有必要在BLL中塞进wrapper,以提高可扩展性。实际上,UI和DAL的分离已经提供了足够的扩展能力,而无须BLL中的wrapper来增加负担。

sting liu

unread,
Dec 11, 2008, 9:24:40 AM12/11/08
to pon...@googlegroups.com
考虑未来业务的扩展,一定需要wrapper和空空的BLL吗?或者说,只有wrapper才能提供未来业务的扩展吗?
"只有"这个绝对量的提问是没有意义的. 

这里,我给出另一个途径。我们可以先去掉BLL,或者将DAL中与业务有关的内容移到BLL中。也可以理解为由UI直接访问DAL。这样,所有基本的业务对象的操作由UI直接从DAL处获得,比如业务对象的增删改查等等。然后,对于复杂的业务,比如需要组合多个基本业务操作的逻辑,在UI中实现。随着业务的复杂化,某些业务逻辑会由多个UI所共享,那么就将这些代码分离出来,形成一个模块,或者UI中的亚层。或许这个亚层也可以称为BLL(实际上它叫什么有什么差别吗?),但按照标准的分层理论,它够不成一个层次,因为他没有将上层和下层隔完全离开。不过,这对于实际应用没有什么影响。即便在那些标准的BLL中,也存在很多操作,如复杂的查询,需要绕开DAL直接访问数据库。因而这些三层模型也并非严格意义上的分层。
如果这个程序只有你一个人写,那你写在哪里都没问题.如果是一个team来做的话,我不知道你是准备让他们自由发挥,还是team来硬性规定用这种方式来写.即使你统一team来做这个规定,项目被别的team接收或者进入新人的话又要投入精力,过一年后来个2期,你又找到了一个可用的方法来写,你会用你新的方式还是这次方式的?既然已经有这样的一个事实默认的n层分布,而且大家都熟悉它,为什么你还要再来自己发明一套?

再有,这些wrapper的存在对代码修改的难度有多大的影响呢?让我们假设有一个业务逻辑A被UI调用,并且只有一个使用点。现在可以考察一下,当A发生变动时(暂时不考虑接口的变动,因为接口一变,UI通常都得改),究竟是A放在UI中修改起来麻烦呢,还是A放在单独的BLL中麻烦呢?实际上,两这是一样的。因为无论A在哪里,我只需修改一个地方,要么在UI里,要么在BLL里。
基本上,在wrapper中对修改的代码进行的测试要比ui容易的多,也就影响到你代码修改的效率.开发不只是写代码.

所以说,并没有必要在BLL中塞进wrapper,以提高可扩展性。实际上,UI和DAL的分离已经提供了足够的扩展能力,而无须BLL中的wrapper来增加负担。
绝对的必要肯定不会,但是相对优异还是有的. 至于你说wrapper是负担,我不知道到底有多少人在手工写这些wrapper.没有小工具?自己写个.
 

supern lee

unread,
Dec 11, 2008, 9:31:58 AM12/11/08
to pon...@googlegroups.com
过渡设计?

2008/12/11 莫华枫 <longsh...@gmail.com>

莫华枫

unread,
Dec 11, 2008, 7:37:51 PM12/11/08
to pon...@googlegroups.com


2008/12/11 sting liu <xbl...@gmail.com>

考虑未来业务的扩展,一定需要wrapper和空空的BLL吗?或者说,只有wrapper才能提供未来业务的扩展吗?
"只有"这个绝对量的提问是没有意义的. 
很多程序员不就是这么认为的吗?

这里,我给出另一个途径。我们可以先去掉BLL,或者将DAL中与业务有关的内容移到BLL中。也可以理解为由UI直接访问DAL。这样,所有基本的业务对象的操作由UI直接从DAL处获得,比如业务对象的增删改查等等。然后,对于复杂的业务,比如需要组合多个基本业务操作的逻辑,在UI中实现。随着业务的复杂化,某些业务逻辑会由多个UI所共享,那么就将这些代码分离出来,形成一个模块,或者UI中的亚层。或许这个亚层也可以称为BLL(实际上它叫什么有什么差别吗?),但按照标准的分层理论,它够不成一个层次,因为他没有将上层和下层隔完全离开。不过,这对于实际应用没有什么影响。即便在那些标准的BLL中,也存在很多操作,如复杂的查询,需要绕开DAL直接访问数据库。因而这些三层模型也并非严格意义上的分层。
如果这个程序只有你一个人写,那你写在哪里都没问题.如果是一个team来做的话,我不知道你是准备让他们自由发挥,还是team来硬性规定用这种方式来写.即使你统一team来做这个规定,项目被别的team接收或者进入新人的话又要投入精力,过一年后来个2期,你又找到了一个可用的方法来写,你会用你新的方式还是这次方式的?既然已经有这样的一个事实默认的n层分布,而且大家都熟悉它,为什么你还要再来自己发明一套?
这不需要发明什么,从重复代码中分理处公用模块并非什么高深的方法,也不是什么新发明。任何合格的程序员都应当具备这种能力。事实上这种做法仅仅是将BLL作为UI中的一个亚层,而不是完整的一个层次。从而去除与DAL重复的那部分。这算不上新的发明。

再有,这些wrapper的存在对代码修改的难度有多大的影响呢?让我们假设有一个业务逻辑A被UI调用,并且只有一个使用点。现在可以考察一下,当A发生变动时(暂时不考虑接口的变动,因为接口一变,UI通常都得改),究竟是A放在UI中修改起来麻烦呢,还是A放在单独的BLL中麻烦呢?实际上,两这是一样的。因为无论A在哪里,我只需修改一个地方,要么在UI里,要么在BLL里。
基本上,在wrapper中对修改的代码进行的测试要比ui容易的多,也就影响到你代码修改的效率.开发不只是写代码.
同样的东西,在wrapper中测试,比在UI中的一个模块中测试更加方便高效?业务逻辑的抽象本身就提供了这种方便和高效,代码放在哪里对此会有什么影响吗?

所以说,并没有必要在BLL中塞进wrapper,以提高可扩展性。实际上,UI和DAL的分离已经提供了足够的扩展能力,而无须BLL中的wrapper来增加负担。
绝对的必要肯定不会,但是相对优异还是有的. 至于你说wrapper是负担,我不知道到底有多少人在手工写这些wrapper.没有小工具?自己写个.
写wrapper容易,改wrapper也容易。但是当DAL发生变化时,除了需要修改DAL外,还要修改wrapper,同样的东西需要改两个地方,是不是负担呢?

假设这样一种情景,UI原本只是创建一个业务对象,直接访问DAL。后来需求变了,需要组合其他的一些DAL操作。那么此时我在一个模块中添加一个函数,或者是类,执行这个新的业务逻辑。然后由UI调用。这同在wrapper中修改代码有多大的差别呢?
相反,那些wrapper中绝大多数的操作(实际上,我所看到的是全部)都是转发。而且,这些业务逻辑的修改也是很稀有的事(一般都会添加新的。因为wrapper所转发的都是业务对象的增删改查,太基本了不敢随便动。如果这些操作逻辑有变,要改的应该是DAL。)。为了这些稀有的事维护这么一套wrapper,值得吗?

ge wu

unread,
Dec 11, 2008, 7:46:52 PM12/11/08
to pon...@googlegroups.com
仰望老莫   :D

2008/12/12 莫华枫 <longsh...@gmail.com>

Linker

unread,
Dec 13, 2008, 2:00:52 AM12/13/08
to pon...@googlegroups.com
我见过些程序,开口闭口"封装"。
但是为什么要封装却说不出来。
其实,要不要封装(分层)和做爱要不要戴套是一个问题。
要看和谁做,在哪里做,有没有时间买套子,是否事先准备了套子。。。


2008/12/12 ge wu <wug...@gmail.com>

Iceberg

unread,
Dec 14, 2008, 10:31:11 AM12/14/08
to TopLanguage
On 12月12日, 上午8时37分, "莫华枫" <longshank...@gmail.com> wrote:
> 2008/12/11 sting liu <xbl...@gmail.com>
>
> > 再有,这些wrapper的存在对代码修改的难度有多大的影响呢?让我们假设有一个业务逻辑A被UI调用,并且只有一个使用点。现在可以考察一下,当A发生变动 时(暂时不考虑接口的变动,因为接口一变,UI通常都得改),究竟是A放在UI中修改起来麻烦呢,还是A放在单独的BLL中麻烦呢?实际上,两这是一样的。因为 无论A在哪里,我只需修改一个地方,要么在UI里,要么在BLL里。

>
> > 基本上,在wrapper中对修改的代码进行的测试要比ui容易的多,也就影响到你代码修改的效率.开发不只是写代码.
>
> 同样的东西,在wrapper中测试,比在UI中的一个模块中测试更加方便高效?业务逻辑的抽象本身就提供了这种方便和高效,代码放在哪里对此会有什么影响吗?


这一个线索的贴子讨论得相当精彩。我潜水看这个系列,印证我自己在工作中的体会,也都受益匪浅。呵呵。近期在做WEB开发,WEB也有MVC三个层次的
划分,其中的View、Control、Model就大致对应于UIL、BLL、DAL吧。基本能够套用这个讨论之中的很多观点。

关于这个“同样的东西,在wrapper中测试,比在UI中的一个模块中测试,哪种更加方便高效”的问题,就我近期遇到的体会补充两句。同样的东西,放
在View中实现与放在Control中实现,代码量基本持平;将来业务变更时,要更改也是只更改一个地方。这方面,没有什么差别。但从测试的角度说,
业务逻辑最好还是放在Control层,会比较方便。因为大多数WEB框架的control层都是实现为一批功能函数,可以用成熟的代码级
UnitTest手段来测试。如果是放在View层(即UI层),那么测试的时候,就涉及到要在一定程度上模拟浏览器的行为甚至模拟鼠标键盘操作才能进
行测试,就比较麻烦了。

说上面这些,意思并不是用于反对老莫的一系列观点。相反,我对老莫的核心观点是相当赞同的。以我的理解,老莫观点的核心在于“按需应变,不要拘泥于某种
特定的框架或套路;应该抓住的是本质的东西,例如DRY”。我相信老莫假如是本来把一些业务逻辑写在UI层,而当他遇到类似上面描述的测试需求时,他也
会毫不犹豫地把一些代码重新抽取出来放入一个中间层(不管这个层叫做什么名称)。

Yester

unread,
Dec 14, 2008, 1:35:56 PM12/14/08
to pon...@googlegroups.com
师傅领进门, 修行在个人。

2008/12/14 Iceberg <ice...@21cn.com>

莫华枫

unread,
Dec 14, 2008, 7:44:20 PM12/14/08
to pon...@googlegroups.com

这一个线索的贴子讨论得相当精彩。我潜水看这个系列,印证我自己在工作中的体会,也都受益匪浅。呵呵。近期在做WEB开发,WEB也有MVC三个层次的
划分,其中的View、Control、Model就大致对应于UIL、BLL、DAL吧。基本能够套用这个讨论之中的很多观点。

关于这个"同样的东西,在wrapper中测试,比在UI中的一个模块中测试,哪种更加方便高效"的问题,就我近期遇到的体会补充两句。同样的东西,放
在View中实现与放在Control中实现,代码量基本持平;将来业务变更时,要更改也是只更改一个地方。这方面,没有什么差别。但从测试的角度说,
业务逻辑最好还是放在Control层,会比较方便。因为大多数WEB框架的control层都是实现为一批功能函数,可以用成熟的代码级
UnitTest手段来测试。如果是放在View层(即UI层),那么测试的时候,就涉及到要在一定程度上模拟浏览器的行为甚至模拟鼠标键盘操作才能进
行测试,就比较麻烦了。
呵呵,反对也欢迎:),毕竟反对声可以让人更全面地检视问题。:)
我前面只是试图说明这些业务逻辑的位置对其代码工程性没有什么影响,举了极端的例子,从而忽略了测试方面的问题。多谢iceberg提醒。
iceberg说的对,把业务部分的东西做成单独的函数更加易于测试。那么问题就变成,这些函数放在UI层和BLL层有多大的差别?
对于仅仅是增删改之类的最简单的业务操作,DAL已经提供了完整的功能,那么无须一个函数再转发一下,完全可以由UI代码直接调用。而这些DAL的功能可以直接测试。而对于那些组合多个DAL的操作,用一个函数包装起来,这更加合理。这些函数包含了复杂逻辑,并非简单的转发,也就不存在重复。(如果对于那些可以直接调用DAL完成的操作,也用一个函数包装一下,那么显然毫无必要。)
这些函数可以构成一个模块,为UI提供服务。是单独地用一个BLL存放这个模块呢?还是放在UI里呢?还是放在DAL里呢?说实在的,这是个问题吗?按照标准的分层模型,是不能跨层访问的。那么,这个模块只能作为一个亚层存在。可是我们实在没有必要这么死板地套用模型。反倒是一些非技术的因素指导模块的安置。如果开发团队按层分工,那么这个模块放在UI里比较合适。因为UI和业务逻辑的关系更加紧密,变化的相互影响比较大,放在同一个人的手里可以减少沟通成本。
相反,使用wrapper的做法倒是严格地遵守分层模型的规定,为了避免跨层访问,而用wrapper进行填充。这太教条了。
软件原本就是一个不规整的怪物。:)

sting liu

unread,
Dec 14, 2008, 9:00:57 PM12/14/08
to pon...@googlegroups.com
可能前面的我没有很详细的看.讨论的前提我默认为是现在流行的web application.我现在是做java的.目前的开发的话.增删改查都是有工具自动生成skelton,工作量不是很大.而主要的时间还是在业务逻辑实现上.就是所谓的wrapper上,我们称为service类.按照分层概念将业务层抽取接口暴露方法.这类系统主要的功能逻辑都是放在server端的.
我不知道你说的这些论点是基于什么样的系统里.太宽泛而言的话没有针对性也不具适应性.对系统的看法上我比较赞同javaeye上一篇文章的的观点:web应用而言,只有b系统和s系统.就是在浏览器上的系统和服务器端系统.现在的web应用基本向这两方面开始分化演变,换个说法就是现在的bs系统是b系统和s系统结合的应用,就比如我们现在用的gmail.
仔细分析的话到底什么是ui呢,其实b端的东西未必都是ui,如今的webapp上b端已经开始系统化.
就UI上的逻辑实现的话.希望老莫能够详细的说一下.

2008/12/15 莫华枫 <longsh...@gmail.com>

hyifeng

unread,
Dec 14, 2008, 10:10:43 PM12/14/08
to TopLanguage
iceberg:
MVC 和 UI-BL-DA 三层模型不是一回事。

莫华枫:
用Petshop这种业务逻辑空虚的例子说明不了问题,一般的系统都不会简单到完全是UI直接到CURD。
一个设计良好的可重用的系统抽象,自然而然会呈现出这三层。
抛开部署、伸缩性、应对变化等因素,如果开发人员足够自觉,的确没有必要如此严格分开这三层的依赖关系,
但可惜的是你不能寄望开发人员在急于修改或新增一个个feature的时候违背了这种精神,系统就会非常迅速地腐化。

莫华枫

unread,
Dec 15, 2008, 12:33:17 AM12/15/08
to pon...@googlegroups.com


2008/12/15 sting liu <xbl...@gmail.com>
可能前面的我没有很详细的看.讨论的前提我默认为是现在流行的web application.我现在是做java的.目前的开发的话.增删改查都是有工具自动生成skelton,工作量不是很大.而主要的时间还是在业务逻辑实现上.就是所谓的wrapper上,我们称为service类.按照分层概念将业务层抽取接口暴露方法.这类系统主要的功能逻辑都是放在server端的.
我说的这种三层模型主要用在传统的c/s结构上。web app上也用,但是很少,而且更不适合。面对松散的web,这种基于oop的三层模型,需要花费很大的代价用于跨进程的远程对象调用。
如果service类不是简单地转发DAL的调用,而是存在更复杂的业务逻辑,那么就不是我说的wrapper。问题是在那些只作转发的wrapper上。
我不知道你说的这些论点是基于什么样的系统里.太宽泛而言的话没有针对性也不具适应性.对系统的看法上我比较赞同javaeye上一篇文章的的观点:web应用而言,只有b系统和s系统.就是在浏览器上的系统和服务器端系统.现在的web应用基本向这两方面开始分化演变,换个说法就是现在的bs系统是b系统和s系统结合的应用,就比如我们现在用的gmail.
仔细分析的话到底什么是ui呢,其实b端的东西未必都是ui,如今的webapp上b端已经开始系统化.
就UI上的逻辑实现的话.希望老莫能够详细的说一下.
说实在的,真的没什么可以详细说的,前面基本上已经说过了。业务逻辑在哪里并不重要,关键的是业务的抽象。抽象所得业务逻辑放在一个模块中,这个模块放在b端也是不错断选择。我甚至觉得这样更好,便于修改和变化。至于模块,js上建立模块,也无非就是做函数和对象,要紧的是代码组织好。
b端分不分层,完全视具体情况而定,能分则分。实际上,我觉得根本没有必要费心考虑分层的问题,做好模块化,能分曾的地方,自然就会分出层来。
我最近就在尝试着不让s端的主代码涉及具体的业务,只提供基础的抽象服务,用脚本或配置文件存放基本的业务元素,比如业务对象描述、复杂查询等等。通过s端开放的接口(走ajax)获得数据,在b端做进一步处理和展示。b端的业务操作进一步抽象,构造各种模块。
本质上,这是一种按照软件功能划分的层次,而不像三层模型那样以业务逻辑为核心的分层。s端执行数据服务,而b端则执行业务流程和数据展示。

Atry

unread,
Dec 15, 2008, 6:23:00 AM12/15/08
to pon...@googlegroups.com
2008/12/9 莫华枫 <longsh...@gmail.com>
另一方面,理论上,三层模型是为了提高系统的灵活性,把系统的变化局限在某个层次。但这仅仅是理论上的。要发挥三层模型的最佳效能,最好是上层的变化比下曾更大。但在实际中,反倒是一些基础的东西,比如业务类的结构,逻辑关系,变化的很多。同时,业务逻辑和UI的关系也比预想的紧密。因而,很多原本BLL的操作、约束被移到UI,从而造成BLL的空置。
这一点我深有感触。我理解这是一个依赖关系的问题。正确的依赖关系是"经常变化的模块依赖于不经常变化的模块"。若UI比数据库变化得更频繁,则三层模型倒还凑合,但实际情况数据库和UI都变化频繁,分层就很蠢,反而是横向分割更好。

Atry

unread,
Dec 15, 2008, 6:24:21 AM12/15/08
to pon...@googlegroups.com
我说的横向分割指的是数据、业务和UI之间高耦合,但不同的业务之间独立。

2008/12/15 Atry <pop....@gmail.com>

Atry

unread,
Dec 15, 2008, 6:32:19 AM12/15/08
to pon...@googlegroups.com
或者这么说吧,软件的模块划分有两种方式。第一种方式是"经常变化的模块依赖于不经常变化的模块"。第二种方式是"两个经常变化的模块依赖于不经常变化的接口"。第二种情况也可以看成第一种的特例。

2008/12/15 Atry <pop....@gmail.com>

Justin L

unread,
Dec 15, 2008, 8:21:34 PM12/15/08
to TopLanguage
我想,我不是做web服务的,我从我的思考角度出发。
BLL层应该包含哪些东西呢,有很明显的原则性的东西来指示区分吗?而DAL仅仅是负责数据的访问和存储。
按照我的想法,举个例子。例如在一个提供并行处理的服务中(例如云计算,这个时髦),存储数据这个东西,是属于业务处理范畴呢,还是属于数据访问范畴
呢?我的意思是:用户指定想保存到某个地点。(例如,考虑到云计算分布式特点,数据存储位置有不确定性,说不定有公司会提供专门的存储业务,提供让用户
满意的数据安全)
我想,这个应该是属于BLL层的东西吧。
那么DAL这个基本上就是属于数据访问的封装。不管是关系型数据、还是基于对象的。
如果这两者区分的原则不清楚,那再去做设计,也很难看到简洁、必要、清晰的方案了。

On 12月9日, 下午2时59分, "莫华枫" <longshank...@gmail.com> wrote:
> 我见到过一种三层模型,分为UI、BLL、DAL。此外还有一组Model类,用于封装实际的业务对象。这没什么特别的,多数三层模型都是这样。但是,这种三层 模型有个特殊的地方,对于每一个描述业务对象的Model类,在BLL里基本上都有一个类对应,实时查询、检索等等操作。差不多就是个管理类吧。但如果深入到这 些类内部,发现它们只是一组wrapper。其内部保有一个对象,该对象的类来自于DAL。而BLL中的这些类上的操作在DAL相对应的类上都有。BLL类实际 只是在转发操作,而真正工作的,是DAL中的那一组类。


>
> 我觉得很奇怪,为什么要凭空锻造出这么一组毫无用处的类呢?得到的答案是:从MS的案例中学来的。诧异之余,我未免心中起疑,MS再差劲,也不至于直眉瞪眼地破 坏DRY吧。肯定是这些家伙学艺不精,会错MS的意了。
>

> 不久前,公司来了一个应聘者,走的是.net <http://xn--0iv967a7jr.net>


> 路线,在外做过几个项目。我们出编程题考他,结果他做的程序的结构也是三层模型,居然和上面所说的结构一模一样,也是在BLL中放了一组无用的wrapper。 关键是,此人和那个三层模型的创造者此前完全不可能相识。那么,只有两种可能。要么是他们以同样的方式错误理解了MS的案例;要么就是MS的案例本来就是这样的 。
>

> MS的案例究竟如何,我没有考证过,也无意去关心。我想到的是一个程序员如何开始学习编程。很多程序员喜欢从案例开始学习。从抄写代码,慢慢变为小修小改,然后 逐步理解,最后开始单飞。但是,这里有个问题。程序员就像小鸟。小鸟会把第一眼看见的会动的东西当成妈妈,而程序员则会把最初学习的案例当成典范。
>
> 因而,程序员最初开始的编程学习就显得非常重要。一旦定型,以后很难改掉。一个程序员最初不应该从某些应用型的案例中开始学习。更多地应该把精力放在基本功上, 像基本的编程原则、算法、范式等等。并且从一些小案例开始,比如写个小算法、小工具等等。此时的程序员还缺乏判断力,无法认清那些复杂案例中的问题和陷阱。上面 提到的这种三层模型,我相信ms原意并非如此。但是,涉世不深的程序员很容易误解其中的用意,然后加以发挥。在没有正确引导的情况下,误入歧途。
>
> 可是,现实往往是残酷的。很多程序员并没有机会获得完善的培训。他们在大学里没有打好基础,来到工作单位就立刻投入开发工作,很多都是从接手和维护其他人的代码 开始。企业希望以此快速地培养出程序员。但是这也往往使得那些代码中原本存在的缺陷被新手们充分吸收。老程序员往往无暇顾及那些新人。有时,甚至他们自身也未曾 意识到存在的问题。这样,错误就一代接一代地传了下去。
>
> 指出了这些问题之后,我却不知道有什么好方法能够破解。我能想到的唯一方法就是,趁自己工作前,或者技术定型之前,打好基础,开拓眼界。不要迷信案例,带着怀疑 的眼光和基本原则来看待他们。多问(不是在这里哈),多想,多google,多wiki,还有多上TopLanguage。:P
>
> --
> 反者道之动,弱者道之用
> m...@seaskysh.com
> longshank...@gmail.comhttp://blog.csdn.net/longshanks/

up duan

unread,
Dec 15, 2008, 9:12:12 PM12/15/08
to pon...@googlegroups.com


2008/12/16 Justin L <ice...@gmail.com>

我想,我不是做web服务的,我从我的思考角度出发。
BLL层应该包含哪些东西呢,有很明显的原则性的东西来指示区分吗?而DAL仅仅是负责数据的访问和存储。
按照我的想法,举个例子。例如在一个提供并行处理的服务中(例如云计算,这个时髦),存储数据这个东西,是属于业务处理范畴呢,还是属于数据访问范畴
呢?我的意思是:用户指定想保存到某个地点。(例如,考虑到云计算分布式特点,数据存储位置有不确定性,说不定有公司会提供专门的存储业务,提供让用户
满意的数据安全)
我想,这个应该是属于BLL层的东西吧。

你的原则是正确的,DAL就是数据访问,UI就是用户交互,而剩下的所有都是BLL。现在的问题是:数据访问是什么?的确有一些组件不属于数据访问的范畴【比如:各种codec,压缩解压缩等】,但是,你所说的业务处理范畴究竟有多少不是数据访问的?比如:我的业务中有一项是:给工作满三年的员工工资提高10%,这究竟是不是数据访问?难道关系型数据库提供的功能就不应该用而必须在BLL层自己实现一个吗?性能能保证?正确性能保证?开发效率能保证?我觉得这才是对DRY的最大的违背。在MIS或者类MIS系统中,绝大多数所谓的业务逻辑其实就是数据访问或者修改的逻辑,之所以现在流行三个层次,我觉得主要原因在于ORM的流行导致程序员很大程度上对于数据库系统的陌生乃至漠视导致的,他们习惯于对象,而不太关注或者恐惧ER。

对于你提出的数据保存到某一点这个例子,且不说跟云计算的冲突性,只说数据store于某一点这个本质,你怎么会觉得这属于BLL而不是数据访问呢?你能明确的说说数据访问究竟干什么吗?在我的看法中,位置是数据访问非常低层次的东西了,SQL之类的接口已经不再提供这种我认为比较低层次的访问性了。

zero

unread,
Dec 15, 2008, 10:40:44 PM12/15/08
to TopLanguage
问题关键就在这里,我们不要说很大的概念什么云计算什么的,举一个切实的例子。


就像有人说的,给员工提高10%的工资,是通过sql 做还是通过java类做,这一层究竟算业务逻辑还是数据逻辑,问题关键或者核心我想就在这里。

On 12月16日, 上午10时12分, "up duan" <fixo...@gmail.com> wrote:
> 2008/12/16 Justin L <icer...@gmail.com>

图灵刘江

unread,
Dec 16, 2008, 9:04:35 AM12/16/08
to TopLanguage
这话很紧要啊。刚才读到《蒙文通学记》,71页说到:“读书,入门最重要。
路子错了,不仅事倍功半,可能终身不得其门而入。”

蒙同学经学学廖平,佛学从欧阳竟无,又曾问故于章太炎,不亏是高手,
精辟啊。

可惜现在市面上的入门书,好的不多,就是有好的,也卖得一般,郁闷。
一个典型,C语言卖得最火的,还是谭浩强……

On 12月9日, 下午2时59分, "莫华枫" <longshank...@gmail.com> wrote:

>
> 因而,程序员最初开始的编程学习就显得非常重要。一旦定型,以后很难改掉。

up duan

unread,
Dec 16, 2008, 9:08:39 AM12/16/08
to pon...@googlegroups.com
当然,还有一个办法就是坚持"科学精神",不断地怀疑和否定,不断地加深加宽自己的知识面,这样也可以避免入门路径的偏差导致见识的偏差。

2008/12/16 图灵刘江 <liuj....@gmail.com>

莫华枫

unread,
Dec 16, 2008, 7:12:02 PM12/16/08
to pon...@googlegroups.com


2008/12/16 up duan <fix...@gmail.com>

当然,还有一个办法就是坚持"科学精神",不断地怀疑和否定,不断地加深加宽自己的知识面,这样也可以避免入门路径的偏差导致见识的偏差。
我觉得这是现在真正难的地方。我们的教育从小就不鼓励"科学精神","不断地怀疑和否定"更是大逆不道。然后整个社会为此买单。唉。
Reply all
Reply to author
Forward
0 new messages