重构(Refactoring)就是通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。
遗留系统,程序员们为了快速完成需求和上线而写出了最基本的代码,而在功能的不断扩充过程中,以打补丁的方式对代码进行扩充,中间还会面临着开发人员的变更和离职。逐渐的,代码就会越来越臃肿,渐渐的变得难以维护。
程序员,特别是做遗留系统维护工作的程序员,清理代码是他们的日常工作之一。这是基本工作,是必须要做的。首要的任务是为此系统编写单元测试,搭建重构的测试保护网,并能够形成可读与可工作的测试用例文档。在发现遗留系统中代码的坏味道时,及时地对代码进行重构,并保证重构的小步前进,小规模的、独立的、稳妥的对代码进行结构上的调整,每次调整完后都要进行测试,确保你没有改变代码的行为特征——功能和以前一样,只是代码上看着不同。重构模式和现代化的 IDE里的重构工具使重构变得容易、安全和代价低廉。
单元测试是指,对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作,这里的最小可测试单元通常是指函数或者类。单元测试都是以自动化的方式执行。
单元测试的用例是一个“输入数据”和“预计输出”的集合。需要针对确定的输入,根据逻辑功能推算出预期正确的输出,并且以执行被测试代码的方式进行验证。即“在明确了代码需要实现的逻辑功能的基础上,什么输入,应该产生什么输出”。
上面介绍了什么是重构与单元测试。接下来我们通过一个实际示例来学习一一下重构与单元测试。先来聊一下该示例的使用场景,就是我们现在街上最常见共享场景之一——共享充电宝,一个客户去共享充电宝机柜上进行消费,下方的程序是给店主用的,来根据用户在不同地段所借充电宝和数量来计算该用户消费的金额和积分。需求很简单而且也不难理解。
一、创建充电宝计费项目
1.我们先来创建这个共享充电宝项目。启动Visual Studio 2019,在启动页面点击右下方的“创建新项目”按钮。如下图。
2.在“创建新项目”界面中,选择”c#”、“Windows”、“控制台”,此时会出现两个项目模板,一个是.net Core的,一个是.Net Framework的。我选择了.NET Core的控制台应用程序。如下图。
3.在“配置新项目”界面中,填写“项目名称”为LeasePowerBank,在“位置”中输入你的目录名称。然后点击“下一步”按钮。如下图。
4.在“其他信息”界面中选择“目标框架”下拉框。如下图1。在下拉框中选择“NET50”,然后点击“创建”按钮。如下图2.
图1
图2.
5. 到此一个充电宝计费项目已经创建成功了,接下来我们要添加相应的代码。首先我们给出了充电宝类的实现。在PowerBank类中有充电宝的地段种类(静态常量):高人流地段(每天人流x>10000)、中人流地段(3000<x<10000)、低人流地段(x<3000),然后有两个成员变量/常量是PriceCode(价格代码)、Title(充电宝名称),最后就是我们的构造方法了。该PowerBank类比较简单,在此就不做过多的赘述了。代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LeasePowerBank { /// <summary> /// 充电宝类 /// </summary> public class PowerBank { //地段人流量种类 static int LowTraffic = 0;//低人流量地段 static int MiddleTraffic = 1;//中人流量地段 static int HighTraffic = 2; //高人流量地段 int PriceCode; //价格代码 string Title;//充电宝名称 public PowerBank(string title,int priceCode) { PriceCode = priceCode; Title = title; } } }
其次,在定义了PowerBank类之后,接下来就是租赁类Rental,这个Rental类的职责就是负责统计某个充电宝租赁的时间。下方就是这个租赁类,该类也是比较简单的,其中有两个字段,一个是租用了哪个地段的充电宝,另一个就是租赁的时间了。代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LeasePowerBank { /// <summary> /// 租赁类 /// </summary> public class Rental { PowerBank Power ; //充电宝名称 int RentedTime;//租赁时间 public Rental(PowerBank powerbk,int rentedTime) { Power = powerbk; RentedTime = rentedTime; } } }
第三,我们来设计一个消费者类,也就是Customer类。在Customer类中有消费者的名字Name和一个数组,该数组中存储的就是租赁充电宝的集合。其中的Statement()方法就是结算该用户的消费金额与积分的方法,并将结果进行打印。在此我们需要了解的需求是每种地段的充电宝价格的计价方式以及积分的计算规则。
充电宝价格计算规则:
低人流量地段的充电宝:1小时之内含1小时,每小时收费1元,每天最多收取12元
中人流量地段的充电宝:每小时间 3元,每天最高收取24元
高人流量地段的充电宝:每小时收取5元,每天最高收取50元
积分计算规则:
每消费一元,积分加1,高人流量地段的充电宝,租赁超过4小时,每一元积1.5积分
Statement()方法中所做的事情就是根据上面的计算规则,根据用户所租赁的充电宝的不同来进行金额的计算和积分的计算的。Customer类的具体代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LeasePowerBank { /// <summary> /// 客户类 /// </summary> public class Customer { string Name;//用户名称 public List<Rental> listRentals=new List<Rental>();//用户租赁的充电宝 public Customer(string name) { Name = name; } public void AddRental(Rental rental) { listRentals.Add(rental); } /*充电宝价格计算规则: 低人流量地段的充电宝—1小时之内含1小时,每小时收费1元,每天最多收取12元 中人流量地段的充电宝--每小时间 3元,每天最高收取24元 高人流量地段的充电宝—每小时收取5元,每天最高收取50元 积分计算规则: 每消费一元,积分加1,高人流量地段的充电宝,租赁超过4小时,每一元积1.5积分 */ public string Statement() { decimal totalAmount = 0M; int frequentRenterPoints = 0; string result = $"{Name} 的租赁账单: "; foreach (var item in listRentals) { decimal amount = 0M; switch (item.Power.PriceCode) { case 0: amount = item.RentedTime; if (item.RentedTime>12) { amount = 12; } break; case 1: amount = item.RentedTime*3; if (item.RentedTime > 24) { amount = 24; } break; case 2: amount = item.RentedTime * 5; if (item.RentedTime > 50) { amount = 50; } break; default: break; } //总价计算 totalAmount += amount; } //计算积分 if (item.Power.PriceCode == PowerBank.HighTraffic && item.RentedTime > 4) { frequentRenterPoints += (int)Math.Ceiling(amount * 1.5M); } else frequentRenterPoints += (int)Math.Ceiling(amount); result += $"总金额为:{totalAmount} "; result += $"你本次获得了:{frequentRenterPoints}积分 "; return result; } } }
6.如果你看代码不太直观的话,下面我使用了Visual Studio 2019中的代码图来说明上述三个类中的依赖关系。具体如下所示: