“代码千万行,注释第一行;编程不规范,同事两行泪”;"道路千万条,安全第一条。代码不规范,亲人两行泪。"在技术圈广为盛传,可见代码不规范让程序员们是多么的头痛。
如何让你的代码整洁而优雅?如何写出让同事膜拜的漂亮代码?
01
何谓重构
重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
重构与性能优化有很多相似之处:两者都需要修改代码,并且两者都不会改变程序的整体功能。两者的差别在于其目的:重构是为了让代码“更容易理解,更易于修改”。这可能使程序运行得更快,也可能使程序运行得更慢。在性能优化时,我只关心让程序运行得更快,最终得到的代码有可能更难理解和维护,对此我有心理准备。
02
两顶帽子
Kent Beck提出了“两顶帽子”的比喻。使用重构技术开发软件时,我把自己的时间分配给两种截然不同的行为:添加新功能和重构。
添加新功能时,我不应该修改既有代码,只管添加新功能。通过添加测试并让测试正常运行,我可以衡量自己的工作进度。
重构时我就不能再添加功能,只管调整代码的结构。此时我不应该添加任何测试(除非发现有先前遗漏的东西),只在绝对必要(用以处理接口变化)时才修改测试。
03
为何重构
我不想把重构说成是包治百病的万灵丹,它绝对不是所谓的“银弹”。不过它的确很有价值,尽管它不是一颗“银弹”,却可以算是一把“银钳子”,可以帮你始终良好地控制自己的代码。重构是一个工具,它可以(并且应该)用于以下几个目的。
重构改进软件的设计
重构使软件更容易理解
重构帮助找到bug
重构提高编程速度
04
何时重构
在我编程的每个小时,我都会做重构。有几种方式可以把重构融入我的工作过程里。
预备性重构:让添加新功能更容易
帮助理解的重构:使代码更易懂
捡垃圾式重构
有计划的重构和见机行事的重构
长期重构
复审代码时重构
何时不应该重构
05
重构的挑战
每当有人大力推荐一种技术、工具或者架构时,我总是会观察这东西会遇到哪些挑战,毕竟生活中很少有晴空万里的好事。你需要了解一件事背后的权衡取舍,才能决定何时何地应用它。
我认为重构是一种很有价值的技术,大多数团队都应该更多地重构,但它也不是完全没有挑战的。有必要充分了解重构会遇到的挑战,这样才能做出有效应对。
06
重构,第一个示例
在本书第1版中,我使用的示例程序是为影片出租店的顾客打印一张详单。放到今天,很多人可能要问了:“影片出租店是什么?”为了避免过多回答这个问题,我翻新了一下示例,将其包装成一个仍有古典韵味又尚未消亡的现代示例。
设想有一个戏剧演出团,演员们经常要去各种场合表演戏剧。通常客户(customer)会指定几出剧目,而剧团则根据观众(audience)人数及剧目类型来向客户收费。该团目前出演两种戏剧:悲剧(tragedy)和喜剧(comedy)。给客户发出账单时,剧团还会根据到场观众的数量给出“观众量积分”(volume credit)优惠,下次客户再请剧团表演时可以使用积分获得折扣—你可以把它看作一种提升客户忠诚度的方式。
该剧团将剧目的数据存储在一个简单的JSON文件中。
plays.json…
1{
2"hamlet": {"name":"Hamlet","type":"tragedy"},
3"as-like": {"name":"As You Like It","type":"comedy"},
4"othello": {"name":"Othello","type":"tragedy"}
5}
他们开出的账单也存储在一个JSON文件里。
invoices.json…
1[
2{
3"customer":"BigCo",
4"performances": [
5{
6"playID":"hamlet",
7"audience":55
8},
9{
10"playID":"as-like",
11"audience":35
12},
13{
14"playID":"othello",
15"audience":40
16}
17]
18}
19]
下面这个简单的函数用于打印账单详情。
1functionstatement (invoice, plays) {
2lettotalAmount = 0;
3letvolumeCredits = 0;
4letresult = `Statementfor${invoice.customer}\n`;
5const format = new Intl.NumberFormat("en-US",
6{ style:"currency", currency:"USD",
7minimumFractionDigits: 2 }).format;
8for(letperf of invoice.performances) {
9const play = plays[perf.playID];
10letthisAmount = 0;
11switch (play.type) {
12case"tragedy":
13thisAmount = 40000;
14if(perf.audience > 30) {
15thisAmount += 1000 * (perf.audience - 30);
16}
17break;
18case"comedy":
19thisAmount = 30000;
20if(perf.audience > 20) {
21thisAmount += 10000 + 500 * (perf.audience - 20);
22}
23thisAmount += 300 * perf.audience;
24break;
25default:
26throw new Error(`unknowntype:${play.type}`);
27}
28// add volume credits
29volumeCredits += Math.max(perf.audience - 30, 0);
30// add extra creditforevery ten comedy attendees
31if("comedy"=== play.type) volumeCredits += Math.floor(perf.audience / 5);
32//printlineforthis order
33result += `${play.name}:${format(thisAmount/100)}(${perf.audience}seats)\n`;
34totalAmount += thisAmount;
35}
36result += `Amount owed is${format(totalAmount/100)}\n`;
37result += `You earned${volumeCredits}credits\n`;
38returnresult;
39}
用上面的数据文件(invoices.json和plays.json)作为测试输入,运行这段代码,会得到如下输出:
1StatementforBigCo
2Hamlet:$650.00 (55 seats)
3As You Like It:$580.00 (35 seats)
4Othello:$500.00 (40 seats)
5Amount owed is$1,730.00
6You earned 47 credits
更多重构手法推荐大家有时间看下这本书,写出更漂亮的代码,京东、当当链接都给你准备好了。
每日金句:“需要资料的请加JAVA高阶学习Q群:8515318105;就能领取2019年java架构师进阶学习资料和BAT面试题。”
网友评论