1. 给函数起个好名字
动词 + 关键词 : 例如把JUnit中的assertEquals改成 assertExpectedEqualsActual(expected, actual)可能会更好些,大大减轻了记忆参数顺序的负担
2. 函数参数
最理想的参数数量是 0(零参函数)–》其次是1(单参函数)–》 再次是2 (双参函数)–》 应该尽量避免3(三参函数)
如果函数看起来需要2个,3个或者3个以上参数,那说明其中一些需要封装成为类了。
例如:
1 2
| Circle makeCircle(double x, double y, double radius); Circle makeCircle(Point center, double radius);
|
当一组参数被传递,就像上例中的x和y,往往就是该有自己名称的某个概念的一部分(学会将一组基本变量抽象成为对象)
3. 改变输入参数,从而导致参数成为了输出参数
这一点在java在写业务代码时经常会碰到,改变了输入参数,造成很多迷惑行为
例如:
1 2
| public void appendFooter(StringBuffer report);
|
如果不添加注释会产生疑惑:是给report追加东西还是,把report添加到其它东西上?所以写成如下会比较好理解
4. 杜绝标识参数
标识参数(True|False)非常丑陋。使用标记参数做了两件事情,则应该拆分它。
1
| render(boolean isSuite) => renderForSuite() , renderForSingleTest()
|
5. 提取try/catch块
try/catch块在函数中很丑陋,将包裹在try/catch中的代码提取出来,减少try/catch的臃肿程度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| private void writeToClient(HttpServletResponse response, String filePath) { try (ServletOutputStream os = response.getOutputStream(); InputStream stream = new FileInputStream(file)){ File file = new File(filePath); response.setContentLength((int)file.length()); int length = 0; byte[] buff = new byte[1024]; while ((length = stream.read(buff)) > 0) { os.write(buff, 0, length); } } catch (IOException e) { log.error("Download happened error", e); } finally { if (!file.delete()) { log.error("The file {} is failed to be deleted!", filePath); } } }
|
提取try中的业务代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| private void writeToClient(HttpServletResponse response, String filePath) { try (ServletOutputStream os = response.getOutputStream(); InputStream stream = new FileInputStream(file)){ doWrite(os, stream, reponse, filePath) } catch (IOException e) { log.error("Download happened error", e); } finally { if (!file.delete()) { log.error("The file {} is failed to be deleted!", filePath); } } }
private void doWrite(ServletOutputStream os, InputStream stream, HttpServletResponse response, String filePath){ File file = new File(filePath); response.setContentLength((int)file.length()); int length = 0; byte[] buff = new byte[1024]; while ((length = stream.read(buff)) > 0) { os.write(buff, 0, length); } }
|
4. 理解OO
面向对象的设计有一个原则(我最初是从 Grady Booch 那里听到的):“如果觉得设计太复杂,那就生成更多对象。”这种说法既违反直觉,又简单得可笑,但我发现它很有用(“生成更多对象”通常等同于“再增加一层抽象”)。总的来说,如果发现有些地方代码很乱,就要考虑用哪种类可以清理代码。通常清理代码带来的副作用是使系统更灵活并且结构更好。
5. 清理火车代码
杜绝过长的调用链如:
隐藏委托关系(Hide Delegate)
迪米特法则(Law of Demeter),这个原则是这样说的:
- 每个单元对其它单元只拥有有限的知识,而且这些单元是与当前单元有紧密联系的;
- 每个单元只能与其朋友交谈,不与陌生人交谈;
- 只与自己最直接的朋友交谈。
1 2 3 4 5 6 7 8 9 10
| book.getAuthor().getName();
class Book { ... public String getAuthorName() { return this.author.getName(); } ...} String name = book.getAuthorName();
|
要想摆脱初级程序员的水平,就要先从少暴露细节开始。声明完一个类的字段之后,请停下生成 getter 的手,转而让大脑开始工作,思考这个类应该提供的行为。
基本类型偏执
对于返回值,和参数能封装为类就封装为类;
比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public double getPrice() { ...}
double price = this.getPrice(); if(price <= 0){ throw new RuntimeException("价格不能为0"); }
class Price{ double price; public Price(final double price) { if (price <= 0) { throw new RuntimeException("价格不能为0"); } this.price = price; } }
|
Comment and share