best practice 封装存储过程(续4)——越抽象,就越容易封装变化。


 

上回书 Tony  says

看起来它似乎并没有改善不同层次代码的耦合关系,以前例看,所有的针对数据库的调用都是通过C#层的RunProc实现的,似乎它本身就充当了隔离层的作用,为什么还要增加一个无用的层delete_dept(1)呢? 

 

你的问题提的很好。我会更加细致的解答你的疑问。

 

简单说,delete_dept语义上比RunProc更加抽象,没有那么多的细节,因此更加容易封装变化。

 

首先,我们从使用者角度考虑,本地函数或者Runproc方法的时候,那一个更加简单。

 

是的,函数也是有客户的,函数的客户就是它的调用者。我们看一个函数设计的好不好,关键要看对客户的代码是否隐藏不必了解的细节。

还是以代码为例来看,  当客户代码,比如button_click()内调用 

  RunProc("delete_dept",1)我们看RunProc需要用户了解那些信息:

 

1.          这是一个调用存储过程的方法

2.          这个存储过程名称是delete_dept

3.          这是部门的编号是1

而使用代码delete_dept(1)客户并不需要知道在RunProc内需要了解的信息12。当然3还是需要了解的。

我们再细致些,对比下两者的语义:

 

 

语义

delete_dept(1)

我要删除一个id=1的部门。

  RunProc("delete_dept",1)

(当前数据库连接下),调用一个叫做delete_dept存储过程,参数为1

 

我们每个人都做过客户,比如说做过装修的主人家,都知道和装修公司说,“恩,不要给我说那么多的细节,我就关心是否达到了我想要的效果”。对吗?

体验以下两者的语义,使用delete_dept(1),和使用RunProc("delete_dept",1)相比,哪一个让你这个客户更加轻松?

 

因此,用语义分析上看,delete_dept(1)只是说做什么,RunProc则是说如何做。这就是层次上的差别了。

 

其次,我们看看两者如何封装变化。要知道,软件的的最大问题在于适应变化。一个客户需求的变化,最理想的情况下,我们希望在一个地方解决,而不是到处修改。

那么,删除一个部门会有什么的变化呢?我经过整理,找到大家提到了的几个可能的变化

1.          存储过程命名不合适,需要修改

2.          需要考虑更多的错误处理

3.          需要用户给出更多的参数,比如 让用户自己输入部门的名称,加以验证。

 

为了便于分析,我们假设整个程序有3个地方需要调用删除部门。伪代码如下:

 

Button1_click()

{

     delete_dept(1) 

}

Button2_click()

{

     delete_dept(1) 

}

 

Button3_click()

{

     delete_dept(1) 

}

 

delete_dept(id)

{

   RunProc('delete_proc',id);

}

限于篇幅,我们只能举出这样的小例子:请稍微想象以下,这些调用(如button-click 可能分布在很多单元内。

 

我们来对比下两种情况下需求的变动,会导致多少个函数需要更新:

 

 

 

修改点

修改点

情况

localfunction

Runproc

1

1

3

2

1

3

3

5

4

 

看到这个数字,你并不觉得怎么样。可是当你把3换成100的时候,也许就不这么想了。

 

 

修改点

修改点

情况

localfunction

Runproc

1

1

100

2

1

100

3

102

101

合计

103

301

 

就是说,3个变化同时发生的情况下,前者需要修改103个函数,后者是301次。如果仅仅头两个发生的话,那么前者2次,后者200次——这就是差别——当调用增加的时候,我们考虑问题必须进入新的视角:在比较少调用的情况下,代码差点没有关系;当调用很多的时候,我们必须锱铢必较

 

100个调用是不算什么了。我们的checkError方法,我统计是1900多次调用,检查权限是990多次调用,更不要说更加基本的函数了。

 

前两情况,体现了我们一直以来 的一个最高理想——客户的一个需求变化,只要在一个地方就可以修改完毕。其中这样的变化仅仅看名称就看得出来。使用delete_dept(1),比RunProc需要了解的细节更好,因此,当细节变化发生的时候,前者更加容易封装变化。

 

可是,有人一定会说,第三种情况修改的点还是很多啊。你的封装不管用啊?

首先,不管有多少引用,两者的修改点都仅仅差一个,因此可以认为等同。其次,封装仅仅可以封住我们考虑到的情况,而不是所有情况。这种情况下,整个函数因为加入了参数,语义都已经发生了变化,是不是叫做delete_dept都不一定了,此时还要封装,就勉为其难了。再好的封装都不能解决所有问题,我们要做的是,尽可能的封装变化,而不是封装全部变化——这是永远也不可能的。

发布了4 篇原创文章 · 获赞 0 · 访问量 2301
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章