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

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

时间:2023-03-11 16:37:06浏览次数:47  
标签:重构 plays perf C# PerformanceEnhance 案例 int result public

书接上文...

之前我把Performance类加了一个amount属性,后来我想了下.这个Performance属于输入类,最好是不要动它,因为一般我们的输入结构是不能够随我们自己的心意随意变动的.

如果我们确实需要给它加入属性的话.我们最好自己定一个中间转换类.所以我还原了这个Performance,另外加了一个PerformanceEnhance类.仅仅为了加这个amount就加了一个类,我又不甘心,所以我又想了很久还有什么其他的属性或方法可以加入这个类.

突然就灵光乍现.我认为其实如果一个Performance给定了剧目和观众数量的话.那么这个演出的价格和积分实际上就已经确定了.(软考里面不是学过函数依赖嘛,好像数据库范式也有提到字段依赖啥啥的)那么价格和积分可以作为这个PerformanceEnhance的属性

第一步如下

 1     public class Performance
 2     {
 3         public string playID;
 4 
 5         public int audience;
 6     }
 7 
 8     public class PerformanceEnhance
 9     {
10         public  int amount { get; set; }
11 
12         public  int VolumeCredits { get; set; }
13 
14         public Play play;
15     }
View Code

第二步呢.就是一个新的非常重要的重构手法了:以多态取代条件表达式

面向对象的编程中其实有非常多的条件表达式可以用多态取代.

在这个案例中呢。我们注意到在计算金额的时候我们根据play.type做了switch处理.在计算积分的时候我们又根据play.type做了if判断.

试想一下如果哪天又多了一个新的内容比如演出的需要支付演出税.我们又得再加条件表达式分别处理.甚至如果哪天剧目的类型又加了一种 童话剧。那我们一改就要改3个地方条件表达式,非常容易改错以及遗漏。所以我们要以多态取代条件表达式重构

把计算金额和积分的方法,移动到子类中,然后我们只需要在一开始创建对象之前,像工厂模式一样swtich case 创建好对象。后面就自然而然,不用的对象用不同的方法了。再也不要写额外的条件表达式了。有点抽象。还是具体看这个例子吧。

目前有两种类型,comedy和tragedy,所以我们至少需要新建两个子类,那么父类怎么抽出来呢。我又灵光一现,嗯,第一步我们不是引入了一个PerformanceEnhance类嘛,它可不可以作为父类呢。先来试试吧

 1     public class PerformanceEnhance : Performance
 2     {
 3         public PerformanceEnhance(Performance performance, Dictionary<string, Play> _plays)
 4         {
 5             audience = performance.audience;
 6             playID = performance.playID;
 7             play = _plays[performance.playID];
 8         }
 9         public virtual int amount { get; set; }
10 
11         public virtual int VolumeCredits { get; set; }
12 
13         public Play play;
14     }
15 
16     public class ComedyPerformance : PerformanceEnhance
17     {
18 
19         public ComedyPerformance(Performance performance, Dictionary<string, Play> _plays) :base(performance, _plays)
20         {
21         }
22 
23         public override int amount
24         {
25             get
26             {
27                 int result = 30000;
28                 if (audience > 20)
29                 {
30                     result += 1000 + 500 * (audience - 20);
31                 }
32 
33                 return result;
34             }
35         }
36 
37         public override int VolumeCredits
38         {
39             get
40             {
41                 int result = Math.Max(audience - 30, 0);
42 
43                 result += audience / 5;
44 
45                 return result;
46             }
47         }
48     }
49 
50     public class TragedyPerformance : PerformanceEnhance
51     {
52         public TragedyPerformance(Performance performance, Dictionary<string, Play> _plays) : base(performance, _plays)
53         {
54         }
55         public override int amount
56         {
57             get
58             {
59                 int result = 40000;
60                 if (audience > 30)
61                 {
62                     result += 1000 * (audience - 30);
63                 }
64 
65                 return result;
66             }
67         }
68 
69         public override int VolumeCredits
70         {
71             get { return Math.Max(audience - 30, 0); }
72         }
73     }
View Code

这里步子有点大了。。。这是个不好的习惯,最好还是按照书上的方法一步一步来。

解释一下

我把AmountFor()和VolumeCredits()这两个方法根据其中的条件表达式分别移入了ComedyPerformance和TragedyPerformance的amount和VolumeCredits属性.这一步应该都能看懂没啥问题

为啥PerformanceEnhance的构造函数要引入(Performance performance, Dictionary<string, Play> _plays)两个参数呢?嗨,这不是为了把原本performance里面的playid和audience取出来嘛,_plays 是另外用来给这个play字段赋值么

这里需要展示一下如何通过performance和_plays创建PerformanceEnhance,并给其赋值

 1         private PerformanceEnhance GetEnhance(Performance perf, Dictionary<string, Play> _plays)
 2         {
 3             PerformanceEnhance performanceEnhance;
 4             switch (_plays[perf.playID].type)
 5             {
 6                 case "comedy":
 7                     performanceEnhance = new ComedyPerformance(perf, _plays);
 8                     break;
 9                 case "tragedy":
10                     performanceEnhance = new TragedyPerformance(perf, _plays);
11                     break;
12                 default:
13                     throw new Exception($"Unknown Type:{_plays[perf.playID].type}");
14             }
15 
16             return performanceEnhance;
17         }
View Code

另外对于这个StatementData类我也有想法,

之前StatementData.Performances 字段用的是new List<Performance>(),我们已经换成new List<PerformanceEnhance>()

还专门建了一个EnrichStatementData方法用来给StatementData充入数据.其实duck不必

我们直接在StatementData的构造函数中干这些事就可以了呀 我们把TotalAmount()和TotalVolumeCredits()方法分别移入StatementData.TotalAmount属性和StatementData.TotalVolumeCredits属性中。然后在构造函数中给Customer和StatementData.Performances赋值

 1     public class StatementData
 2     {
 3         public string Customer;
 4 
 5         public List<PerformanceEnhance> Performances = new List<PerformanceEnhance>();
 6 
 7         public int TotalAmount
 8         {
 9             get
10             {
11                 int result = 0;
12                 foreach (var perf in Performances)
13                 {
14                     result += perf.amount;
15                 }
16 
17                 return result;
18             }
19         }
20 
21         public int TotalVolumeCredits
22         {
23             get
24             {
25                 int result = 0;
26                 foreach (var perf in Performances)
27                 {
28                     result += perf.VolumeCredits;
29                 }
30 
31                 return result;
32             }
33         }
34 
35         public StatementData(Invoice invoice, Dictionary<string, Play> _plays)
36         {
37             Customer = invoice.Customer;
38             foreach (var perf in invoice.performances)
39             {
40                 Performances.Add(GetEnhance(perf, _plays));
41             }
42         }
43 
44         private PerformanceEnhance GetEnhance(Performance perf, Dictionary<string, Play> _plays)
45         {
46             PerformanceEnhance performanceEnhance;
47             switch (_plays[perf.playID].type)
48             {
49                 case "comedy":
50                     performanceEnhance = new ComedyPerformance(perf, _plays);
51                     break;
52                 case "tragedy":
53                     performanceEnhance = new TragedyPerformance(perf, _plays);
54                     break;
55                 default:
56                     throw new Exception($"Unknown Type:{_plays[perf.playID].type}");
57             }
58 
59             return performanceEnhance;
60         }
61     }
View Code

因此我们创建中转数据的时候也不用那个CreateStatementData()方法和EnrichStatementData()方法了

 1         public string Statement()
 2         {
 3             StatementData statementData = new StatementData(_invoice, _plays);
 4             return RenderPlainText(statementData);
 5         }
 6 
 7         public string HtmlStatement()
 8         {
 9             StatementData statementData = new StatementData(_invoice, _plays);
10             return RenderHtml(statementData);
11         }
View Code

这样调用多简单。完成

最后编译、启动

 

 OK 完成.这个案例到这里就结束啦。

 谢谢大家花了时间读这个狗屁不通的帖子。

上一篇:《重构-改善既有代码设计案例》案例之C#版(4)

 

标签:重构,plays,perf,C#,PerformanceEnhance,案例,int,result,public
From: https://www.cnblogs.com/flylittlebaby/p/17206304.html

相关文章