上回书 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内需要了解的信息1,2。当然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都不一定了,此时还要封装,就勉为其难了。再好的封装都不能解决所有问题,我们要做的是,尽可能的封装变化,而不是封装全部变化——这是永远也不可能的。
相关推荐
Unity插件——Best HTTP 封装好的网络插件,节省自己写http的时间
Best_Practice: Scale-Resolving Simulations in ANSYS CFD.pdf
sap best practice(应付帐款会计)
best practice for convolution neural network
Top 10 Best Practice for SSIS 开发设计
Best Practice Guide - Classical Migration of SAP NetWeaver AS ABAP to SAP HANA
SQL Server DBA best practice by EMC2
Smalltalk Best Practice Patterns(Kent Beck).pdf
avaScript: Best Practice Kindle Edition by James Kolce (Author), Moritz Kroger (Author), Ivan Curic (Author), Samier Saeed (Author), Jeff Mott (Author), M. David Green (Author), Craig Buckler (Author)...
Android Design Patterns and Best Practice,Android设计模式学习资料(pdf+epub+mobi+code)。
This classic book is the definitive real-world style guide for better Smalltalk programming. This author presents a set of patterns that organize all the informal experience successful Smalltalk ...
这是上海翰纬信息管理咨询有限公司智慧的结晶。希望对大家学习工作有所帮助。更多资料还会陆续上传敬请期待,您可以致电021-4008868806进行询问,或登陆翰纬官网www.sinoserviceone.com进行了解~
This PPT is about the most important features and critical best practices presented by Steven Feuerstein, a guru of Oracle PL/SQL language.
gpdb-best-practice
NULL 博文链接:https://sap.iteye.com/blog/333924
UiPath机器人开发的相关教程。This document outlines the standards and best practices to be considered by RPA developers when building automation processes with UiPath
NULL 博文链接:https://sap.iteye.com/blog/336651
你所不知道的C++,临时变量、重载、模板、异常……等等你所不知道的细节
j2ee Best Practice.pdf
如果sql script所用的排序区过大, 超过了oracle设定的sort_area_size, 这时oracle将会使用磁盘排序, 一般使用temporay tablespace的空间, 这时也就会出现disk_reads. Disk_reads所花的代价是很大的,一般是不建议...