首页 > 代码库 > 规范化程序设计的实例(1)

规范化程序设计的实例(1)

提出问题
首先要感谢实验室的章老师提出一个有现实应用的背景与问题:
智能手机中,用户十分关心已经使用的流量, 希望可以查看已经使用的网络流量; 本程
序主要实现过去 1 分钟以及过去 1 小时已经使用的网络流总量,并且希望整个设计具备一定
的可扩展性,比如可以很方便地扩展到统计过去一天的网络流总量。
另外,程序在使用时还需要其运行速度和内存的使用情况。
学习目标:
1.  如何撰写可读性强的代码;
2.  基于性能和存储空间的代码优化设计;
3.  基于设计灵活性的代码优化设计;
概要:
1.  要解决的问题是统计过去一分钟以及一小时的网络流量总和;
2.  我们忽略界面等非核心的要素,关注核心数据结构和实现;
3.  在设计过程中,以接口设计为主导,往往是先给出接口设计,然后再考虑具体实现,
当然实际的设计过程中,可能是同时进行;
4.  设计实现的过程包含三个版本,从简单版本开始,迭代改进,直到最终的版本;第
一个版本只关注功能的实现,不考虑其他因素;第二个版本在前者基础上,考虑性
能和内存空间的优化;最后一个版本进一步对内存空间使用进行优化,并且同时考
虑设计的灵活性;


定义类接口

<span style="font-size:14px;">class MinuteHourCounter{
//add a count
void Count(int num_bytes);//return the count over this minute
int MinuteCount();
//return the count over this hour
int HourCount();
}</span>
在实现这个类之前,让我们首先看一看这些名字和注释,看看有哪些地方有改进的地方。

MinuteHourCounter 这个类名很好,很专门、具体,并且容易读出来。
Count()是有问题的,因为有人会认为它的意思是“返回所有时间里的总的计数”。这个名字有点违反直觉。问题是count既是个名词又是个动词,所以既可以是“我想要得到你所见过的所有样本的计数”意思也可以是“我想要你对样本进行计数”的意思。

下面有几个名字可供替代count():

1、Increment()

是会误导人的,因为它意味着一个只会增加的值。

2、Observe()

还可以,但是感觉有点模糊。

3、Record()

既是动词又是名称,不好

4、Add()

它既可以是“以算术方法增加”的意思,也可以是“添加到一个数据列表的意思”,在该情况下,两种情况兼有,所有适合。

所以修改为void Add(int num_bytes)。

但是参数名num_bytes太有针对性了,可以采用更加通用的count,既简单、通用并且暗示“非负数”。


版本 1:
下面是主要的接口设计,其中的 Add()对新到来的流量事件进行记录,而 MinuteCount()和
HourCount()分别用于统计过去一分钟和一小时的网络流量和;

<span style="font-size:14px;">class MinuteHourCounter{
//add a count;
void Add(int count);
//return the accumalated count over the last 60 seconds;
int MinuteCount();
//return the accumalated count over the last 3600 seconds;
int HourCount();
}
//下面是上述接口的具体实现:
class MinuteHourCount{
struct Event{
Event(int count, time_t time):count(count), time(time){}
int count;
time_t time;
};
list<Event> events;
public:
void Add(int count){
events.push_back(Event(count, time());
}
int MinuteCount(){
int count = 0;
const time_t now_secs = time();
for(list<Event>::reverse_iterator i = events.rbegin(); i!=events.rend()&&i->time >
now_secs-60; ++i){
count += i->count;
}
return count;
}
int HourCount(){
int count = 0;
const time_t now_secs = time();
for(list<Event>::reverse_iterator i = events.rbegin(); i!=events.rend()&&i->time >
now_secs-3600; ++i){
count += i->count;
}
return count;
}
};</span>
简评:
上述的代码将流量产生作为一个个事件保存起来,MinuteCount()和 HourCount()则根据
时间戳将符合要求的事件进行累加,并且返回累加值。
暂时不考虑设计本身,光考虑代码的可读性而言,上述代码存在如下的问题:
1. int MinuteCount();  这里的循环操作可读性比较差;

2. int MinuteCount()  和 int HourCount()两者之间的代码重复度太大;可以考虑合并抽取;

<span style="font-size:14px;">//对于上述的问题1的改进:
int MinuteCount(){
int count = 0;
const time_t now_secs = time();
for(list<Event>::reverse_iterator i = events.rbegin(); i!=events.rend(); ++i){
if(i->time > now_secs - 60) //将判断条件独立出来,增加可读性;
count += i->count;
else 
break;
}
return count;
}</span>
观察上述的代码,就可以发现很多的代码都是重复的,因此对于上述问题 2 的改进:

<span style="font-size:14px;">int CountSince(int cutoff){
int count = 0;
const time_t now_secs = time();
for(list<Event>::reverse_iterator i = events.rbegin(); i!=events.rend(); ++i){
if(i->time >  cutoff)
count += i->count;
else
break;
}
return count;
}
int MinuteCount(){
return CountSince(time()-60);
}
int HourCount(){
return CountSince(time()-3600);
}</span>
从设计的角度讲,上述代码的主要问题:
1.需要的存储量是无限;因为需要保存到现在的所有的event,所以内存量不可小觑;
2.另外,计算的时间也不固定,如果每秒钟到来的event很多的话, 那么要统计一个小时
的话,需要进行的累加操作数量巨大,耗费的时间很多;