书接上文...
之前我把Performance类加了一个amount属性,后来我想了下.这个Performance属于输入类,最好是不要动它,因为一般我们的输入结构是不能够随我们自己的心意随意变动的.
如果我们确实需要给它加入属性的话.我们最好自己定一个中间转换类.所以我还原了这个Performance,另外加了一个PerformanceEnhance类.仅仅为了加这个amount就加了一个类,我又不甘心,所以我又想了很久还有什么其他的属性或方法可以加入这个类.
突然就灵光乍现.我认为其实如果一个Performance给定了剧目和观众数量的话.那么这个演出的价格和积分实际上就已经确定了.(软考里面不是学过函数依赖嘛,好像数据库范式也有提到字段依赖啥啥的)那么价格和积分可以作为这个PerformanceEnhance的属性
第一步如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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类嘛,它可不可以作为父类呢。先来试试吧
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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,并给其赋值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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赋值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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()方法了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 完成.这个案例到这里就结束啦。
谢谢大家花了时间读这个狗屁不通的帖子。
标签:重构,plays,perf,C#,PerformanceEnhance,案例,int,result,public From: https://www.cnblogs.com/flylittlebaby/p/17206304.html