2、函数参数列表
最理想的参数数量是零(零参数函数),其次是一(单参数函数),再次是二(双参数函数),应尽量避免三(三参数函数)。有足够特殊的理由才能用三个以上参数(多参数函数)----所以无论如何也不要这么做。
1、一元函数的普遍形式
boolean fielExists("MyFile");
上述就是输入一个参数,将其 转换为某一个东西,在输出之。
例如:
InputStream fileOpen("MyFile")
把 String 类型的文件名转换为InputStream 类型的返回值。
如果函数要对输入参数进行转换操作,转换结果就该体现为返回值。实际上
StringBuffer transform(StringBuffer in)
要比
void transform(StringBuffer out)
要强。
标识参数:true false
向函数传入布尔值简直就是骇人听闻。这样做,方法签名立刻变得复杂起来,大声宣布本函数不止做一件事。如果 表示为true将会这样做,标识为false则会那样做。
比如:
render(boolean isSuite)
应该一分为二:
renderForSuite();
renderForSingleTest();
2、二元函数
有两个参数的函数要比一员函数难懂。例如 writeFile(name) 比 writeFiled(outputStream ,name)好懂。
尽管两种情况下意义都很清楚,但第一个只要扫一眼就明白,更好地表达了其意义。第二个就得暂停以下才明白,除非我们学会忽略第一个参数。
当然,有些时候两个参数正好,例如: Point p = new Point(0,0);将相当合理。笛卡尔点天生拥有两个参数,
使用二元函数要付出代价,你应该尽量利用一些机制将其转换成一元函数。例如:
writeFiled方法写成outputStream的成员之一,从而能这样用: outputStream.writeField(name)。或者 ,也可以把outputStream 写成当前类的成员变量,从而无需再传递。还可以分离出类似FiledWrite 的新类,在其构造器中采用outputStream,并且包含一个write方法。
3、三元函数
有三个参数的函数要比二元函数难懂得多。
比如:
assertEquals(message,expected,actual)
有多少次,你读到message,错以为它既是expected呢,每次看到这里,总会绕半天圈子,最后学会了忽略message参数。
4、参数对象
如果函数需要两个、三个或三个以上参数,就说明其中一些参数应该封装为类了。例如:
Circle makeCircle(double x, double y , double radius);
Circle makeCircle(Point center ,double radius);
从参数 创建,从而减少参数数量,当一组参数被共同传递,就像上例中x 和 y那样,往往就是该有自己名称的某个概念的一部分。
同时,给函数去个好名字,能较好地解释函数的意图,以及参数的顺序和意图。
对于一元函数,函数和参数应当形成一种非常良好的动词/名词对形式,例如,write(name)就相当令人人挺,不管这个"name“是什么,都要被"write"。更好的名称大概是writeFiled(name),它告诉我们,"name" 是一个"filed".
assertEquals(message,expected,actual) ';
改成
assertExpectedEqualsActual(expected,actual);
5、被隐藏的副作用
比如:
public class UserValidator{
.....
public boolean checkpassword(String usernName,Srting password){
// 检验密码
//....
// session.initialize();
}
}
副作用在于Session.initialize()的调用。checkPassword 函数,顾名思义,就是用来检查密码的。该名称并未暗示它会初始化该次会话。所以,当某个误信了函数名的调用者想要检查用户有效性,就得冒着抹除现有会话数据的风险。
checkPassword只能在特定时刻调用(换言之,在初始化会话是安全的时候调用的)。如果在不合适的时候调用,会话数据就有可能沉默地丢失。时序性耦合令人迷惑,特备是当它在副作用后面时。如果一定要时序性耦合,就应该在函数名称中说明。在本例中,可以重命名函数为 checkPasswordAndInitializeSession .
6、分隔指令与查询
函数 要么做什么事,要不回答什么事,但二者不可兼得。函数应该修改某对象的状态或是返回该对象的有关信息。两者都干常会导致混乱,例如 :
public boolean set(String attribute,String value);
该函数设置某个指定属性,如果成功就返回true,如果不存在那个属性则返回false,这样就导致了以下语句:
if(set("username","wyy)){}
上述语句可以理解为:
是否之前已设置为wyy吗
或者
设置username是否成功?
真正的解决方案是: 把 指令与询问分隔开来,防止 混淆的发生:
if(attributeExists(“username”){
setAttribute("username","wyy");
}
7、抽离Try/Catch 代码块
Try/Catch 代码块丑陋不堪。他们搞乱了代码结构,把错误梳理与正常流程混为一谈。最好把Try 和 catch 代码块 的主体部分抽离出来,另外形成函数;
public void delete(Page page){
try{
deletePageAndALlReferences(page);
}catch(Exception e){
logError(e);
}
}
public void deletePageAndAllReferences(Page page) throws Exception{
deletePage(page);
registry.deleteReference(page.name);
configKeys,deleteKey(page.name.makeKey);
}
private void logError(Execption e){
logger.log(e.getMessage());
}
函数应该只做一件事,错误处理就是一件事,因此,处理错误的函数不应该做其他事,这意味着如果关键字try在某个函数中存在,它就该是这个函数的第一个单词而且在catch/finally 代码后面不应该有其他内容。
8、别重复自己
重复代码是软件中一切邪恶的根源。许多原则与实践原则都是为控制与消除重复而创建,例如在java中,把某些代码几种到基类中,从而避免了冗余。面向切面编程(AOP)也是消除重复的一种策略.
9、如何写出好的函数
首先写的冗长而负责,有太多缩进和嵌套循环。有过长的参数列表。名称也是随意取的,也会有重复代码。
然后打磨 这些代码。分解函数、修改名称、消除重复。缩短和重新安置方法。拆散类,同时保持单元测试通过。
最后,遵循上述原则,组装好这些函数。
上一篇: linux 管理工具
下一篇: 开通 星空下 博客墙内镜像