首页 > 编程语言 >《重构-改善既有代码设计案例》案例之C#版(1)

《重构-改善既有代码设计案例》案例之C#版(1)

时间:2023-03-10 12:34:29浏览次数:32  
标签:重构 play perf C# thisAmount 案例 audience result string

《重构-改善既有代码设计》是被众多程序员推荐的一本经典。但问题是其中的一些案例是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

相关文章

  • LeetCode306 累加数
    题目描述累加数是一个字符串,组成它的数字可以形成累加序列。一个有效的累加序列必须至少包含3个数。除了最开始的两个数以外,序列中的每个后续数字必须是它之前......
  • seata can not get cluster name in registry config please make sure registry conf
    问题描述使用seata1.6.1,配合dubbo3,以及nacos22023-03-1012:11:15ERRORio.seata.core.rpc.netty.NettyClientChannelManager181reconnect-cannotgetclusternam......
  • unigui中TuniComboBox限制只能选择,不能手工输入的方法
    问题:TuniComboBox限制只能选择,不能手工输入确认清楚了,对于UniComboBo没有任何问题,对于UniDBComboBox,该属性就存在一定的问题,初始前,不能设置为csDropDownList,必须为默......
  • Spring-AOP简介&案例
    Spring-AOP简介&案例1,AOP简介Spring有两个核心的概念,一个是IOC/DI,一个是AOP。对于AOP,我们前面提过一句话是:AOP是在不改原有代码的前提下对其进行增强。1.1什么是AOP......
  • LeetCode|1410. HTML 实体解析器
    题目链接:1410.HTML实体解析器「HTML实体解析器」是一种特殊的解析器,它将HTML代码作为输入,并用字符本身替换掉所有这些特殊的字符实体。HTML里这些特殊字符和它......
  • CentOS 7 部署 HDP 3.3.1
    一、环境服务器名称配置IP地址备注hdp-161-1418核/16G内存/300GSSD硬盘10.32.161.141MGR/Agenthdp-161-1428核/16G内存/300GSSD硬盘10.32.161.142Age......
  • mvc——mvc的概念、service引入、ioc实现
    资料来源于:B站尚硅谷JavaWeb教程(全新技术栈,全程实战),本人才疏学浅,记录笔记以供日后回顾由于是多个视频内容混合在一起,因此只放了第一个链接视频链接知识点1.什么......
  • opencv初学笔记2(颜色提取与转换)
    opencv初学笔记2(颜色提取与转换)在面对完全不认识的一个技术时,茫然是不可避免的。但是在好奇与任务的驱使下,我一点点地去探索opencv的世界,一点点的试错与调试十分枯燥,可是......
  • 一行一行源码分析清楚 AbstractQueuedSynchronizer(二)
    文章比较长,信息量比较大,建议在pc上阅读。文章标题是为了呼应前文,其实可以单独成文的,主要是希望读者看文章能系统看。本文关注以下几点内容:深入理解ReentrantLock公......
  • 测之以恒——代码精进而不觉 | IDCF DevOps案例研究
    有幸作为组长参加了IDCFDevOps案例研究以下是【分享实录】测之以恒——代码精进而不觉|IDCFDevOps案例研究【分享实录】测之以恒——代码精进而不觉|IDCFDevOps......