作者:henry 来源:博客园 酷勤网收集 2008-08-20
软件设计总是很难捉摸,设计虽然不一定决定软件生死,但对软件的开发周期起着非常重要的作用.那怎样的设计能适应软件的变更修改呢?针对我个人而言只能答不知道…因为我在设计的时候只针对现有存在的问题出发,但在后期软件的变更所产生的东西总让人不知所措.虽然设计很难,但有着丰富经验的设计人员总会知道什么时候应该干些什么,而他们的出发点往往不是考虑得非常周全,而是把代码结构变得越简单越好.业务变还是不变我们控制不了,但如何让代码在变更的情况可以方便维护和修改我们还是有一定的能力的.
详话就不多说了,最近似乎很多人谈三层设计的问题;我来热闹一下,但我谈的没有这么广,紧紧是从业务逻辑设计上来说(细节决定成败).
先看以下代码吧:
public void Create(string username, string email) {
}
public void Create(User user) {
}
其实两个方法完成的功能都是一样的,但两者确有着不同之处.对于我个人而言后者对方法约定变更的风险比较低,因为User类提供信息扩展的提供一些保障.是不是什么情况都这样干呢,显然下面的就没有多大意义了.
public bool Login(string username, string pwd) {
}
public bool Login(User user) {
}
所以有一点我们可以紧记,程序是死的但人是活的.
以上是逻辑对外的,那逻辑内部呢?同样的方法
public void Create(string username, string pwd) {
//insert username,userpwd
//dal.call(username,userpwd)
}
public void Create(User user) {
//DBContext.Add(user)
}
以上代码两者的差别又在那里呢,这就留给有兴趣的朋友发言讨论了:)
还有一个话题:我们如何知道做出来的东西能满足以后的需要呢?如果能做到那唯一的可能就是业务的变化是按我们所假设的方向发展下来,但我们是不是永远都这么好运?如果我们的运气不好那怎样办?这个时候估计只有简单可维护的代码才能把我们从井里救出来…
这文章只是提个引子并不是想说这样做设计才是对的,因为设计方法没有最好只有更好.让我们在不停的实践中进步吧.
评论:
任何技巧都不是可以死记硬背的。像楼主所说的这个问题,如果我们有对User进行明确的定义,那么这些设计如何取舍就会变得显而易见。
我们不妨尝试对User进行一下定义:
User对象代表一个登录用户在内存中的映射,它包含了登录用户的各种信息。
那么根据这个定义我们可以很显然的看出:
Login( User user )是多么的荒谬。
不妨尝试对Login方法也进行一个定义:
通过提供登录凭据,将用户身份登录到当前上下文。
所以,Login ( LoginEvidence loginEvidence )才是正确的设计
当然,LoginEvidence === username + password
So,楼主的设计当然就是合理的了。
其实设计比较讲经验,问题是如何总结经验.个人比较认同重构,我也一直这样干.虽然项目中重构的机会不多,但很多时候可以把问题拿到项目外在有时间的情况下重构.
henry: @Ivony...
是的:)在login的描述是很荒谬,只是说明方法约定的设计也是要有技巧.
--------------------------------------------------------
其实我只是想说,有些时候只要你对每一个东西都有一个很明确的定义,你的思路就会自然的清晰,设计也会如行云流水般。
很多糟糕的设计与其说是对需求变更的预计不够,不如说是根本就没想清楚每一个东西的定义。在看别人的设计时,要看明白的不仅仅是怎样设计的,更重要要弄清楚为什么这样设计,弄清楚这些优秀的设计他们对每一个对象的定义是怎样。再自己给这个设计假定很多需求变更,从对象的定义出发看看如何用最小的修改达到效果。
我个人认为没有优秀的设计,也很难假设能有多少变更;
其实有很多是凭经验的,如DAL的设计那样可能没有这方面经验的人员(也许这人没有从事过这方面设计),可能没有考虑到事务协同的问题.但紧紧凭这一点就把这个设计否决是不现实现;如果设计人员在面对这情况下很快就调整过来那这也算是个好的设计.
设计能对不可知道的情况可以进行快速调整的设计才是好的设计.
如果我们什么情况都能了解就更好了:)
关于事务的问题,你可以继续关心我的系列文章,如果不出意外,在后面的文章里面就会谈到从OO到OR搞出了数据库一大堆纠缠不清的关系如何厘清。
不过我习惯在园子里找问题然后加以阐述,如果您能将您所遇到的实际问题列出来,如果有普遍性的话,我就会很乐意直接拿来阐述了。
预计变化是要对业务非常熟悉的,其实并不容易做,所以我觉得演进的设计更有意思,也就是上边一位朋友说的重构的方式,随着对系统的熟悉设计会更加合理,但重构应该是随时发生的,一旦发现变化就应该对相应代码做出重构,始终使代码在当前看来是最简单的最容易维护的
一个函数的参数到底是传一个类进去还是传一组参数进去,其实要看这个函数的责任,也就是这个函数的处理逻辑.如果一个函数它就是对一个类进行操作那么它需要的参数也应该书一个类,如果它对一组数据进行操作那么它需要的也就是一组数据了.回到楼主的问题,Login的逻辑是对一组数据的合法性进行判断,而一个用户他已经是合法的了,所以如果我们将一个User实例传进去从语义上说是错误的.其实这也和二楼的说法是基本一样.而我对楼主的一点异议就是,Create到底要做什么?为什么要传一个User的实例?如果它是创建一个User实例,还是做什么的,我暂时还没有明白.
回复 引用
这倒不是我面对问题.
只是举个情况,设计往往是要面对不可知的情况,既然是不知道我们就不可能去假设.即使是有些东西是可以假设,但也应该有个度(这就完全取决于设计人员的经验)
所以设计越简单越好(不过这个简单其实并不简单,就如你前面提到的要清楚,其实并不容易)
第一情况的Create是描述方法的定义如何降低方法约定变更的风险.
第二情况的Create是表达两中情况在User发生变化后,各自内部的调整情况.
我自己的感觉,在满足自己目前的前提下,留一些适当的余地。
当有新的需求提出,重新检查下有无类似的代码? 如果有,而且发现重写一个新的方法,代码几乎是相同的。那么,重构。
不可能一开始就想到了所有的应用,基本做不到,大部分是在重构的过程中,逐渐体会。随着经历的增长,基本就能体会到在初始写代码的时候,预留一些可扩展的部分。
--引用--------------------------------------------------
Ivony...: --引用--------------------------------------------------
henry: @Ivony...
是的:)在login的描述是很荒谬,只是说明方法约定的设计也是要有技巧.
--------------------------------------------------------
其实我只是想说,有些时候只要你对每一个东西都有一个很明确的定义,你的思路就会自然的清晰,设计也会如行云流水般。
很多糟糕的设计与其说是对需求变更的预计不够,不如说是根本就没想清楚每一个东西的定义。在看别人的设计时,要看明白的不仅仅是怎样设计的,更重要要弄清楚为什么这样设计,弄清楚这些优秀的设计他们对每一个对象的定义是怎样。再自己给这个设计假定很多需求变更,从对象的定义出发看看如何用最小的修改达到效果。
--------------------------------------------------------
来自:http://www.cnblogs.com/henryfan/archive/2008/08/15/1268431.html

