看着这个实例,一起来学重构吧!
2010-08-17 18:54 Aga.J 阅读(2136) 评论(24) 编辑 收藏 举报浏览jake的blog时看到这样一篇文章30 Days of .NET [Windows Mobile Applications] - Day 01: Minutes to Midnight Countdown(午夜倒数器),题目非常吸引人,我点了进去,看到原来这是jake翻译一个叫Chris Craft的人的系列文章,看完了jake这篇文章后,我也去了Chris Craft的blog看了这篇原文,下载了源代码,代码很简单,就一个timer_Tick的函数把所有程序逻辑都搞清楚了,就是计算我们离今天的结束还有多少小时,多少分钟,多少秒,然后一个一个的列举在form上。下面就是timer_Tick的源代码
private void timer_Tick(object sender, EventArgs e)
{
TimeSpan timeSpan = DateTime.Now.Date.AddDays(1) - DateTime.Now;
labelHours.Text = string.Format("{0} of 24 hours left", timeSpan.Hours);
labelMinutes.Text = string.Format("{0} of 60 minutes left", timeSpan.Minutes);
labelSeconds.Text = string.Format("{0} of 60 seconds left", timeSpan.Seconds);
labelTotalMinutes.Text = string.Format("{0} of 1440 total minutes left", timeSpan.TotalMinutes.ToString("#.0"));
labelTotalSeconds.Text = string.Format("{0} of 86400 total seconds left", timeSpan.TotalSeconds);
progressBarTotal.Value = 86400 - (int) timeSpan.TotalSeconds;
progressBarHours.Value = 24 - timeSpan.Hours;
progressBarMinutes.Value = 60 - timeSpan.Minutes;
progressBarSeconds.Value = 60 - timeSpan.Seconds;
progressBarTotalMinutes.Value = 1440 - (int) timeSpan.TotalMinutes;
progressBarTotalSeconds.Value = 86400 - (int) timeSpan.TotalSeconds;
}
一开始看到这个代码的时候我就觉得好长好复杂,作者设定每100ms就会触发timer事件从而来调用这个函数,但是运行起来我们发现,数值上一直在变化的只有秒数,而分钟数则在60秒后才会有变化,小时的话更需要3600秒,但是作者每过100ms就调用这个timer_Tick的函数,使得每次都要对窗体上的控件的内容进行重置,不管数值有没有变,我们每过100ms都会给labelHours或者是progressBarHours等控件的相关属性赋值一次,而经常需要改变的只是labelSeconds 和progressBarSeconds以及progressBarTotalSeconds。所以我就对作者的这份代码进行了----重构。
我想改的时候,我的第一个念头就是---对象!但是我又觉得没那个必要,所有一开始我想是通过if语句来判断秒数是不是过了60秒,从而来修改分钟的数值,用if来判断分钟数是不是过了60分钟来修改hour的数值,但是这样写下来的代码也是很混乱,所以最后我还是使用了对象,使用了面向对象的东西来重构作者的程序。下面是我作的修改
class NotifyElem
{
//每个元素都有一个周期,秒有60秒的周期,分有60分的周期,小时有24的周期
//这里定义的是一个周期事件处理委托
public delegate void CycleEventHandler();
public event CycleEventHandler cycleEvent;
//这里是显示到控件上的文本
string stringToShow;
//这里是每个对象的周期值
int cycleValue;
//每个对象现在的值
double currValue;
public NotifyElem(string stringToShow, int cycleValue)
{
this.cycleValue = cycleValue;
this.stringToShow = stringToShow;
}
public void setCurrValue(double value)
{
currValue = value;
if (value == cycleValue-1 && cycleEvent != null) //一旦注册了委托并且过了一个周期,那么就会激发事件
{
cycleEvent();
}
}
public string getNotifyString() //将作者对label的赋值串进行了函数上的封装
{
return string.Format(stringToShow, currValue);
}
public int notifyValue() //将作者对progressBar的赋值值进行了函数上的封装
{
return cycleValue - (int)currValue;
}
}
我定义了一个NotifyElem的类,类名的意思主要和我们这个程序相关,指的是告知我们现在还有多少秒,多少分钟,多少小时,然后基本的说明在注释里面。使用这个类,我对主窗体的函数做了下面的修改
首先我自己添加一个form_Load的事件处理,目的是要在form运行时初始化所有显示的东西
private void frmM
{
TimeSpan timeSpan = DateTime.Now.Date.AddDays(1) - DateTime.Now;
labelSeconds.Text = string.Format("{0} of 60 seconds left", timeSpan.Seconds);
progressBarSeconds.Value = 60 - timeSpan.Seconds;
labelTotalSeconds.Text = string.Format("{0} of 86400 total seconds left", timeSpan.TotalSeconds);
progressBarTotalSeconds.Value = 86400 - (int)timeSpan.TotalSeconds;
labelHours.Text = string.Format("{0} of 24 hours left", timeSpan.Hours); //剩余的小时,分钟,秒,使用减出来的timeSpan来获得
progressBarHours.Value = 24 - timeSpan.Hours;
labelMinutes.Text = string.Format("{0} of 60 minutes left", timeSpan.Minutes);
progressBarMinutes.Value = 60 - timeSpan.Minutes;
labelTotalMinutes.Text = string.Format("{0} of 1440 total minutes left", timeSpan.TotalMinutes.ToString("#.0"));
progressBarTotalMinutes.Value = 1440 - (int)timeSpan.TotalMinutes;
}
//里面的代码是拷贝自作者的,和second有关的其实可以不用初始化,因为等下会初始化,这里我也没再去重构,为了方便就贴上去了
接下来就是使用我所定义的类
NotifyElem secondElem;
NotifyElem minuteElem;
NotifyElem hourElem;
public frmM
{
InitializeComponent();
secondElem= new NotifyElem("{0} of 60 seconds left",60);
minuteElem= new NotifyElem("{0} of 60 minutes left", 60);
hourElem= new NotifyElem("{0} of 24 hours left", 24);
secondElem.cycleEvent += new NotifyElem.CycleEventHandler(secondElem_ cycleEvent);
minuteElem.cycleEvent += new NotifyElem.CycleEventHandler(minuteElem_cycleEvent);
}
void minuteElem_cycleEvent()
{
TimeSpan timeSpan = DateTime.Now.Date.AddDays(1) - DateTime.Now;
hourElem.setCurrValue(timeSpan.Hours);
updateUI2(labelHours, progressBarHours, hourElem);
}
void secondElem_cycleEvent()
{
TimeSpan timeSpan = DateTime.Now.Date.AddDays(1) - DateTime.Now;
minuteElem.setCurrValue(timeSpan.Minutes);
updateUI2(labelMinutes, progressBarMinutes, minuteElem);
}
主要有三个对象,两个委托。委托的目的是在每个cycle后设置由这个cycle引起的其他时间元素的变化
接下来就是作者代码里的那个函数timer_Tick
private void timer_Tick(object sender, EventArgs e) //使用一个timer时间间隔是100ms,每到这个时间就更新信息。
{
TimeSpan timeSpan = DateTime.Now.Date.AddDays(1) - DateTime.Now; //使用1天后的时间减去现在,得到timeSpan
secondElem.setCurrValue(timeSpan.Seconds);
updateUI2(labelSeconds, progressBarSeconds, secondElem);
}
我只用了三行代码,首先还是要得到timeSpan,然后就调用secondElem.setCurrValue,因为在timer的tick中,经常变的是second,所有把它的setCurrValue和updateUI函数放到这里来调用,然后大家请看setCurrValue的实现。
public void setCurrValue(double value)
{
currValue = value;
if (value == cycleValue-1 && cycleEvent != null) //一旦注册了委托并且过了一个周期,那么就会激发事件
{
cycleEvent();
}
}
每次到了一个周期后,它就会调用委托的事件,而我们在注册委托时就实现了这个事件的处理,
void secondElem_cycleEvent()
{
TimeSpan timeSpan = DateTime.Now.Date.AddDays(1) - DateTime.Now;
minuteElem.setCurrValue(timeSpan.Minutes);
updateUI2(labelMinutes, progressBarMinutes, minuteElem);
}
每个周期后,会对相应的上一级的时间元素进行更新,这里更新了分钟。
private void updateUI2(Label label, ProgressBar p,NotifyElem n)
{
label.Text = n.getNotifyString();
p.Value = n.notifyValue();
} //这里是更新ui的代码,把重复的工作抽取出来吧。但是传入那么多对象貌似不是个好主意
修改完毕,就到这里,我也不想再继续完美它了,虽然还有好几个地方可以完善,可以不用我现在这样复杂,我对作者原来的代码进行了彻底的重构,作者在写这个程序的时候,很好的给我们展示了timeSpan的应用和一般的wm控件的使用,同样也给了我机会展示一下什么是重构,前阵子看Object-Oriented的书,觉得没地方可以实践,现在就加上重构的知识,实践了一次,当然Object-Oriented Design and Analysis 不仅仅只有这点,它所包含的是整个工程,我在这里只是使用了重构的思想,再加上一下OO的原则。大家对这个程序还有什么更好的重构的方法呢?都来说说吧,大家交流交流OO的东西。
作者:Aga.J
出处:http://www.cnblogs.com/aga-j
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
个人学习笔记仅供本人记录知识所用,不属发表性文章。