《重构-改善既有代码设计》是被众多程序员推荐的一本经典。但问题是其中的一些案例是js写的。作为一个c#开发人员,看起来不太习惯。所以特意抄袭了一版C#代码。
我想重构的一个重要的好处就是方便理解,易于修改。所以我就不说这段代码的功能是啥了。好的代码应该就是一眼就能看出是干啥的。如果一段代码理解起来非常困难,那就说明这段代码需要重构了。
不再废话,直接COPY代码(未重构的原始代码),之后会将重构的代码慢慢放出来。重现书中重构的手法以及效果。作为自己学习的记录。
剧目类
Play.cs
1 class Play 2 { 3 public string name; 4 5 public string type; 6 }View Code
演出类
Performance.cs
1 class Performance 2 { 3 public string playID; 4 5 public int audience; 6 }View Code
费用清单类
Invoice.cs
1 class Invoice 2 { 3 public string Customer; 4 5 public List<Performance> performances=new List<Performance>(); 6 }View Code
马戏团类-----唉?好像应该是剧团才对啊,魂淡,算了就当是剧团吧
Circus.cs 在构造函数中的是演示数据
1 using System; 2 using System.Collections.Generic; 3 using System.Globalization; 4 5 namespace Refactoring 6 { 7 public class Circus 8 { 9 private readonly Dictionary<string, Play> _plays; 10 11 private readonly Invoice _invoice; 12 13 public Circus() 14 { 15 _invoice = new Invoice {Customer = "BigCo"}; 16 _invoice.performances.Add(new performance {playID = "hamlet", audience = 55}); 17 _invoice.performances.Add(new performance {playID = "as-like", audience = 35}); 18 _invoice.performances.Add(new performance {playID = "othello", audience = 40}); 19 _plays = new Dictionary<string, Play>(); 20 _plays.Add("hamlet", new Play {name = "hamlet", type = "tragedy"}); 21 _plays.Add("as-like", new Play {name = "As You Like It", type = "comedy"}); 22 _plays.Add("othello", new Play {name = "Othello", type = "tragedy"}); 23 } 24 25 public string Statement() 26 { 27 int totalAmount = 0; 28 int volumeCredits = 0; 29 string result = $"Statement for {_invoice.Customer} \n"; 30 NumberFormatInfo nfi = new CultureInfo("en-US").NumberFormat; 31 nfi.CurrencyDecimalDigits = 2; 32 33 foreach (var perf in _invoice.performances) 34 { 35 Play play = _plays[perf.playID]; 36 int thisAmount = 0; 37 38 switch (play.type) 39 { 40 case "tragedy": 41 thisAmount = 40000; 42 if (perf.audience > 30) 43 { 44 thisAmount += 1000 * (perf.audience - 30); 45 } 46 47 break; 48 case "comedy": 49 thisAmount = 30000; 50 if (perf.audience > 20) 51 { 52 thisAmount += 1000 + 500 * (perf.audience - 20); 53 } 54 55 break; 56 default: 57 throw new Exception($"unknown type:{play.type}"); 58 } 59 60 //add volume credits 61 volumeCredits += Math.Max(perf.audience - 30, 0); 62 //add extra credit for every ten comedy attendees 63 if ("comedy" == play.type) 64 { 65 volumeCredits += perf.audience / 5; 66 } 67 68 //print line for this order 69 result += $"{play.name}: {string.Format(nfi, "{0:C}", thisAmount / 100)}({perf.audience}seats)\n"; 70 totalAmount += thisAmount; 71 } 72 73 result += $"Amount owed is {string.Format(nfi, "{0:C}", totalAmount / 100)}\n"; 74 result += $"You earned {volumeCredits} credits \n"; 75 return result; 76 } 77 } 78 }View Code
最后就是main函数啦
1 private static void Main(string[] args) 2 { 3 string result = new Circus().Statement(); 4 Console.Write(result); 5 Console.Read(); 6 }View Code
执行结果
话说,像我这种不求上进的百度程序猿。一开始觉得这些代码没啥问题啊。甚至还觉得命名挺规范=.=
想要学好重构,就必须拥有一些程序猿的直觉,能够嗅出代码的坏味道。
首先这个Statement方法体太长了。。。如果整个项目都是这种代码,我敢保证最多看半个小时,就觉得累了。。。
所以这里就有 代码的坏味道之: 过长函数
初一看,这个switch分支处理代码就可以提取出来单独作为一个方法,这个技巧就是提炼函数.然后我们就给这个方法取一个方法名,取名自古就是一个讲究的活。最好能够根据名字就知道这个方法是要干什么的。如果项目里面的方法名都是methodA这样随意的话,那阅读这些代码太浪费时间和精力了。还没看两个方法就累了。。这还怎么玩。。。这就是另外一种代码的坏味道:神秘命名。说干就干
1 public string Statement() 2 { 3 int totalAmount = 0; 4 int volumeCredits = 0; 5 string result = $"Statement for {_invoice.Customer} \n"; 6 NumberFormatInfo nfi = new CultureInfo("en-US").NumberFormat; 7 nfi.CurrencyDecimalDigits = 2; 8 9 foreach (var perf in _invoice.performances) 10 { 11 Play play = _plays[perf.playID]; 12 int thisAmount = AmountFor(perf, play); 13 14 //add volume credits 15 volumeCredits += Math.Max(perf.audience - 30, 0); 16 //add extra credit for every ten comedy attendees 17 if ("comedy" == play.type) 18 { 19 volumeCredits += perf.audience / 5; 20 } 21 22 //print line for this order 23 result += $"{play.name}: {string.Format(nfi, "{0:C}", thisAmount / 100)}({perf.audience}seats)\n"; 24 totalAmount += thisAmount; 25 } 26 27 result += $"Amount owed is {string.Format(nfi, "{0:C}", totalAmount / 100)}\n"; 28 result += $"You earned {volumeCredits} credits \n"; 29 return result; 30 } 31 32 private int AmountFor(Performance perf, Play play) 33 { 34 int thisAmount; 35 switch (play.type) 36 { 37 case "tragedy": 38 thisAmount = 40000; 39 if (perf.audience > 30) 40 { 41 thisAmount += 1000 * (perf.audience - 30); 42 } 43 44 break; 45 case "comedy": 46 thisAmount = 30000; 47 if (perf.audience > 20) 48 { 49 thisAmount += 1000 + 500 * (perf.audience - 20); 50 } 51 52 break; 53 default: 54 throw new Exception($"unknown type:{play.type}"); 55 } 56 57 return thisAmount; 58 }View Code
这还不够,对于AmountFor这个方法,还要做两处小的修改.perf这样的缩写对于理解没有任何帮助,所以要把它改回原名aPerformance(不要缩写也是代码美学之一).在方法内部我也觉得thisAmount改为result这样更便于理解。如此一来代码就成了这样
1 public string Statement() 2 { 3 int totalAmount = 0; 4 int volumeCredits = 0; 5 string result = $"Statement for {_invoice.Customer} \n"; 6 NumberFormatInfo nfi = new CultureInfo("en-US").NumberFormat; 7 nfi.CurrencyDecimalDigits = 2; 8 9 foreach (var perf in _invoice.performances) 10 { 11 Play play = _plays[perf.playID]; 12 int thisAmount = AmountFor(perf, play); 13 14 //add volume credits 15 volumeCredits += Math.Max(perf.audience - 30, 0); 16 //add extra credit for every ten comedy attendees 17 if ("comedy" == play.type) 18 { 19 volumeCredits += perf.audience / 5; 20 } 21 22 //print line for this order 23 result += $"{play.name}: {string.Format(nfi, "{0:C}", thisAmount / 100)}({perf.audience}seats)\n"; 24 totalAmount += thisAmount; 25 } 26 27 result += $"Amount owed is {string.Format(nfi, "{0:C}", totalAmount / 100)}\n"; 28 result += $"You earned {volumeCredits} credits \n"; 29 return result; 30 } 31 32 private int AmountFor(Performance aPerformance, Play play) 33 { 34 int result; 35 switch (play.type) 36 { 37 case "tragedy": 38 result = 40000; 39 if (aPerformance.audience > 30) 40 { 41 result += 1000 * (aPerformance.audience - 30); 42 } 43 44 break; 45 case "comedy": 46 result = 30000; 47 if (aPerformance.audience > 20) 48 { 49 result += 1000 + 500 * (aPerformance.audience - 20); 50 } 51 52 break; 53 default: 54 throw new Exception($"unknown type:{play.type}"); 55 } 56 57 return result; 58 }View Code
本篇完成...待续....
标签:重构,play,perf,C#,thisAmount,案例,audience,result,string From: https://www.cnblogs.com/flylittlebaby/p/17202719.html