突然遇到了一个函数设计上面的两难问题,请各位面向对象高手给指点迷津,怎样合理的设计下面的函数,这是我经过抽象简单化后的函数,实际函数所执行的业务规则更加繁琐,这样写出来的函数越来越大,难以维护,我想应该分解成粒度更小的若干个子函数,可是我怎么也找不分解函数的最佳做法,快崩溃了,我已经试图把我的问题描述详尽了,请看懂偶问题的人给偶点儿指点。
Function SearchLog(MyUserName,MyDept)
Dim sql 'sql字符串
Dim LogType '查看日志的类型,值可以是day,week,month,year和空
Dim Day_StartDay,Day_EndDay '按日查询所需要的参数
Dim Week_StartYear,Week_EndYear,Week_StartWeek,Week_EndWeek '按周查询所需要的参数
Dim Month_StartYear,Month_EndYear,Month_StartMonth,Month_EndMonth '按月查询所需要的参数
Dim Year_StartYear,Year_EndYear '按年查询所需要的参数
Dim ViewLogFlag '查看日志的权限,
Dim Dept '按部门查看所需要部门名称
Dim UserName '按用户查看所需要的用户名称
sql = "select * from [Log]  where [ID] is not null "
'是这样的,我来描述一下,当LogType = "day"的时候需要用Day_StartDay,Day_EndDay两个参数,
'当LogType = "week"时需要Week_StartYear,Week_EndYear,Week_StartWeek,Week_EndWeek这几个参数, 
'当LotType = "month"时需要Month_StartYear,Month_EndYear,Month_StartMonth,Month_EndMonth这几个参数,
'当LogType = "year"时需要Year_StartYear,Year_EndYear这几个参数
'当ViewLogFlag = 1的时候可以查看UserName =  MyUserName的日志
'当viewLogFlag = 2的时候可以查看Dept = MyDept的日志
'当viewLogFlag = 3的时候可以查看所有的日志
'也就是说确定了LogType的值后才会知道哪些参数需要用,而另外的好多参数都用不上了,而确定了ViewLogType的值后可以确定当前调用者可以访问哪些人或者部门的日志,也就是权限.
'这个函数怎样设计才会有很好的独立性和封装性呀,不会把所有可能使用的参数都定义成函数的参数吧,那会好长好长的,而且还会浪费好多参数,我用的是ASP语言,没有重载的功能,请问怎么把这个函数分解一下呀,我的目的是分解成可维护性好,且可读性好的一系列函数,你看我写的这个函数开头写那多变量声明,够繁琐吧,这可不是好现象.
'而调用这个函数要具有不同的权限,不同的权限会有不同的授权,就是根据ViewLogFlag来限制,如果ViewDeptFlag = "1",而UserName又不等于MyUserName,这时候函数应该一个错误.
'因为上面声明的参数都是从函数外部传递进来的,从函数里访问外部数据有两个办法,一个是访问全局变量,一个是通过参数,如果用全局变量的话,会让函数封装性不强,而且全局变量改动的话,还得修改这个函数,但是如果用参数传递的话会写十几个参数,这不矛盾了吗,希望大家提供实现这样的任务的最佳实践.
End Function有人说需要2K叉树,我倒。。
有人说可以用一个结构类型的参数。。其它相关讨论无言独上西楼 16:18:43
你应该描述清楚你要做什么,而不是拿一堆代码出来,让人看你的实现 
蛙蛙王子 16:24:58
我要分解这个函数 
蛙蛙王子 16:25:23
让它可读性更好,可维护性更强,就这么简单 
无言独上西楼 16:20:35
你要描述清楚这个函数做什么用的,需求是怎样的 
蛙蛙王子 16:25:52
这种问题不象问1+1等于几这样的问题有确定的答案 
蛙蛙王子 16:26:14
从函数的名字可以看出它完成日志搜索功能 
无言独上西楼 16:22:26
那是废话 
蛙蛙王子 16:27:47
需求就是,执行这个函数需要几个参数,而参数是动态的,并且这个参数还要加上授权指定和权限判断,还有异常处理,以便它的独立性更强,函数本身更健壮,经的起错误的前置条件 
蛙蛙王子 16:31:36
让这个函数分解成粒度更小的几个相关的函数,这个函数通过logtpye参数确定要查看日志的类型,根据viewlogflag参数确定调用者的权限,来指定授权,比如说viewlogflag=1,这时候它只能查看自己的log,但是传递进来的username参数不是它自己,而是其它用户,这时候要抛出一个异常,终止函数并让调用层根据返回码决定下一步的处理。 
蛙蛙王子 16:32:30
我还没说明白?那我接着说 
无言独上西楼 16:27:32
你把过多的职责放到这一个函数中了 
蛙蛙王子 16:35:54
我认为没有,这是任何一个函数必须要做到的,只是一般人写函数的时候忽略了好多,一个函数肯定有它的函数不变量,前置条件,后置条件,在这些条件不对的情况下要分别做些处理,并且出错的时候要保持系统的状态,你不能在函数出现错误的时候已经更改了一半数据库,你要想办法用事务把系统状态回滚到出错前的状态,这些函数必须完成,当然函数可以调用基础架构服务层的服务来完成一些操作,而函数和底层服务是松散耦合的 
无言独上西楼 16:34:16
建议你找本面象对象的书看看 
蛙蛙王子 16:39:31
因为你写的函数可能是需要让别人来调用的,如果你有职业道德的话,你得保证你写的函数优秀且出色,不能在没有单元测试后就发布你的函数,别人调用你的函数出错了,你却不抛出错误,让别人耽误工作,而且难以调试,所以写设计一个好的函数不是非常的简单,你要小心的做任何处理,包括对全局变量,对全局资源的访问和修改,这都有可能影响到其它的开发成员的工作 
tux_gentoo 16:34:38
职业道德  
tux_gentoo 16:34:59
看来是个新人 
无言独上西楼 16:35:18
我觉得你应该去写书,而不是写代码,呵呵 
tux_gentoo 16:36:24
这年头还讲职业道德么 
无言独上西楼 16:36:37
你写了半天,都是一些程序员的基本准则 
蛙蛙王子 16:42:04
我是看过一些面向对象的书才有这样的问题的,我以前写的代码都是象意大利面条似的长而乱,难以维护,我已经认识我我的习惯很不好,所以才和大家讨论这个问题,书本上的知识用到实际中我不会变通呀 
无言独上西楼 16:37:39
简单地说,权限控制应与日志查询是分离的 
蛙蛙王子 16:42:56
我不希望别人说因为我的函数写的有问题而影响他的正常工作,让他不能正常工作 
无言独上西楼 16:37:53
两个完全不同的职责 
蛙蛙王子 16:43:31
有些是程序员的准则,但更多的还是设计一个优秀函数的最佳实践问题 
tux_gentoo 16:38:32
能完成任务就不错了 
tux_gentoo 16:38:43
还管那么多干嘛 
蛙蛙王子 16:44:51
这里的权限控制不是简单的允许和拒绝,而是根据不同的权限给予不同的授权,所以考虑起来很麻烦,而且我还不想把授权硬编码到函数里,这样以后维护还是问题 
蛙蛙王子 16:46:21
能完成任务,我确实已经完成了,但是光那个函数写了不到100行 
无言独上西楼 16:41:58
很好,我已经很多年没有看超过一屏的方法了 
蛙蛙王子 16:47:56
所以我现在认识到函数已经要写的小而独立,便于维护和查看, 
蛙蛙王子 16:48:13
所以我现在认识到函数一定要写的小而独立,便于维护和查看,  
tux_gentoo 16:43:27
我觉的王子看看linux内核源代码好一点 
无言独上西楼 16:43:59
同意,绝对同意 
无言独上西楼 16:44:07
我看过近W行代码 
无言独上西楼 16:44:13
2.2。13的 
tux_gentoo 16:44:37
代码好不好是自己的事情,没有人和你交流的 
蛙蛙王子 16:50:02
现在那个函数里用了好多嵌套的select case,if等以语句,已经难以维护和扩展了,以后会出现的业务变动无法估量,所以必须得把它写好了 
蛙蛙王子 16:52:00
我也知道实现一个功能应该按照业务把他们分成小粒度的业务块, 粒度越小,组合越灵活 。可是真正让我去实现一个任务,这个问题就变的难了, 
蛙蛙王子 16:52:33
尽管有一些知道原则,可是具体到这个函数我就不知道怎么写了

解决方案 »

  1.   

    ASP虽然不支持重载,但是是支持类的。
    写成类,会很简单。
      

  2.   

    Class SearchLog
    Dim LogType
    Dim Day_StartDay
    Dim Day_EndDay
    Dim Week_StartYear
    Dim Week_EndYear
    Dim Week_StartWeek
    Dim Week_EndWeek
    Dim ...其他变量
    Private sql
    Private result
    Private isOpen
    Private Sub Class_Initialize   ' 设置 Initialize 事件。
    ......
    sql = ""
    set result=Server.CreateObject("ADODB.RecordSet")
    end Sub
    Private Sub Class_Terminate
    if isOpen then 
    result.close
    end if
    End Sub
    Function GenSql() '生成根据条件SQL语句
    ......
    sql = "select ... from ....where ...";
    End Function Function Query()
    result.open sql,.....
    End Function .....其他函数End Class
      

  3.   

    差不多算是解决了,没人讨论我结帖了,有兴趣的看看我的解决方法蛙蛙推荐:软件项目开发经验总结之模块开发
    http://club.yesky.com:80/bbs/upload/app/200506/17/51137_1118983303214.doc