源码工作室

目标:通俗的语言说出通俗的技术\n 老婆开了个网店,园子里的优惠,决不假。(http://shop33647060.taobao.com)
posts - 76, comments - 601, trackbacks - 5, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 ::  :: 管理

书店会员销售系统(一)

Posted on 2006-03-09 15:38 源码工作室 阅读(2790) 评论(12)  编辑 收藏 所属分类: 设计模式实践
书店会员销售系统(一)
                     ――OORefactoring and Design Pattern
本节目的:
   
1.         有一场景引入一个问题,学习测试驱动的编程方法,实现基本功能。
书店中的一幕:
      营业员:“请问你有会员卡吗?”
       顾客: “有的。”
       顾客掏出钱包,拿出会员卡递给营业员,营业员输入会员卡号后开始计书的金额,期间有一本书的条形码扫描不出来,营业员手动输入。
       营业员:“一共是126元。”
       顾客递给营业员130元。
       营业员:“收你130元,找你3元。”
    顾客: “你算错了吧?”
    营业员:“噢,不好意思,再给你1元。”(肯定在想哪件漂亮的衣服)
       顾客:  “我想问一下,我会员卡上的积分是多少了?”
       营业员: “你总共消费累计2700元,积分为250点。”
      顾客:    “那我现在享受几折优惠了?”
       营业员: “你现在可以享受8.5折优惠。”
       顾客:      “噢,我知道了,谢谢你。”
 
    顾客提着书离开收银台。
        我想这一场景大家可能都亲身经历过,当然不一定在书店。这就涉及到小店会员销售系统,如果要你编写这么一个系统,你会如何做呢?
       我想你第一个想到的可能是数据库。是的,会员销售系统会记录很多的信息,肯定是要用到数据库的,但后面的叙述我会尽可能地去避免,为了简单而已。
       我们先来分析一下问题。在上面的场景中涉及到一些对象:顾客、营业员、会员卡、书。顾客有顾客的信息,营业员有营业员的信息,会员卡记录了顾客的ID号,书的价格,在这里我先忽略书的价格,而是计算一次消费的金额,饭要一口一口地吃,还有营业员我们也可以先不涉及。

    我们要实现的功能是:根据不同的会员,对其消费的金额进行打折,并累计消费点数,第一版UML图如下:

     
     
XP
的原则是先写测试用例,然后使测试用例通过调试,得出正确的结果,我在这里也就按部就班的这么做。

int main(int argc, char* argv[])
{
       
char szMemberID[MAX_PATH];
       strcpy(szMemberID,
"00000001");
       
float fConsumeSum = 120.0;
       
int      nPoint  = 0;

        CCalculate      
*pCalc = new CCalculate();
       CMember              
*pMember = new CMember(szMemberID,1000.0,75);
       nPoint 
= pMember->GetPoint();
       nPoint 
+= pCalc->CalculatePoint(fConsumeSum);

        fConsumeSum 
= pCalc->CalcMoney(fConsumeSum,pMember);

        pMember
->SetPoint(nPoint);
        assert(nPoint 
== 87);
        assert(fConsumeSum 
== 120.0);

        printf(
"Point = %d\n",nPoint);
        printf(
"ConsumeSum = %f\n",fConsumeSum);
        delete pCalc;
        delete pMember;
        
return 0;
}



       因为篇幅的原因,所有出错处理都不考虑。
       编译一下,很多错误,主要是CCalculateCMember三个类没有创建,那接下来的工作当然是创建它们。
 
 会员类:

class CMember  
{
public:
    CMember(
char *pszID,float fSum,int nPoint);
    
~CMember();
    
int GetPoint();
    
void SetPoint(int nPoint);
    
float GetRebate();
private:
    
char    m_szID[MAX_PATH];
    
float   m_fSum;
    
int     m_nPoint;
}
;
 
CMember::CMember(
char *pszID,float fSum,int nPoint)
{
    
if(pszID)
        strcpy(m_szID,pszID);
    m_fSum 
= fSum;
    m_nPoint 
= nPoint;
}

 
int CMember::GetPoint()
{
    
return m_nPoint;
}

 
void CMember::SetPoint(int nPoint)
{
    m_nPoint 
= nPoint;
}

 
float CMember::GetRebate()
{
    
if(m_nPoint<=100)
        
return 10;
    
else if(m_nPoint>100 && m_nPoint <200)
        
return 9.5;
    
else if(m_nPoint>=200 && m_nPoint <400)
        
return 9;
    
else if(m_nPoint>=400 && m_nPoint <600)
        
return 8.5;
    
else
        
return 8.0;
}



计算类:

class CCalculate  
{
public:
    CCalculate();
    
~CCalculate();
    
int CalculatePoint(float fSum);
    
float CalcMoney(float fSum,CMember  *pMember);
}
;

int CCalculate::CalculatePoint(float fSum)
{
    
int nPoint = (int)(fSum/10);
    
return nPoint;
}

 
float CCalculate::CalcMoney(float fSum,CMember  *pMember)
{
    
float fRebateSum = 0.0;
    fRebateSum 
= fSum * pMember->GetRebate();
    
return fRebateSum;
}


    然后在编译程序,在测试用例中,我们希望的结果是:
    Point = 87
    ConsumeSum = 120.000000
    可运行的结果却是出现assert异常,仔细一查,输出结果为:
    Point = 87
    ConsumeSum = 1200.000000

    原来在折扣上没有除10,修改代码:

float CCalculate::CalcMoney(float fSum,CMember  *pMember)
{
    
float fRebateSum = 0.0;
    fRebateSum 
= fSum * pMember->GetRebate()/10;
    
return fRebateSum;
}


    测试通过,一个基本功能的程序就编写成功了。当然一组测试用例远远是不够的,我这里也就不多写了。人还是人,错误是很难避免的,通过测试用例,能排除一大部分的问题,也为以后的程序改进提供的保障。
    也许会有人说,这样的设计太烂了。可如果需求不改变的话,至此就已经足够了。这不是本节的用意。

参考资料:
Refactoring: Improving the Design of Existing Code》 ――Martin Fowler

Feedback

#1楼    回复  引用    

2006-03-09 15:47 by lovebanyi [未注册用户]
支持

#2楼    回复  引用    

2006-03-09 16:10 by ^.^ [未注册用户]
“一共是126元。”
顾客递给营业员130元。
营业员:“收你130元,找你3元。”

-----呵呵比较幽默营业员得到小费一元.

强烈支持你!!

#3楼 [楼主]   回复  引用  查看    

2006-03-09 16:40 by 源码工作室      
^.^ :已经改过来了。

#4楼    回复  引用    

2006-03-09 19:24 by 无微不至 [未注册用户]
强烈建议给变量定义加一个注释
英语菜的人就看得懂了
非常非常感谢..

#5楼    回复  引用    

2006-03-10 12:13 by drizzt.zhao [未注册用户]
9494,加注释是好的编程习惯 ^_^

#6楼 [楼主]   回复  引用  查看    

2006-03-10 12:45 by 源码工作室      
同志们,这还是第一篇,以后我还会推出来的。
在重构一书中说,代码就是注释,把代码写的很清楚,注释也就可以少了。
我希望在以后的章节中能做到这点。

#7楼    回复  引用  查看    

2006-03-10 14:06 by 塞北的雪      
assert 是做什么用的?

#8楼    回复  引用  查看    

2006-03-11 14:49 by Terrylee      
@源码工作室
在重构那本书中提到,如果代码需要很多注释来解释,那就有可能出现了代码的坏味道。不过有些变量加上一点注释还是必要的:-)

#9楼    回复  引用  查看    

2006-03-13 09:40 by gmsft      
float CMember::GetRebate()
{
if(m_nPoint<=100)
return 10;
else if(m_nPoint>100 && m_nPoint <200)
return 9.5;
else if(m_nPoint>=200 && m_nPoint <400)
return 9;
else if(m_nPoint>=400 && m_nPoint <600)
return 8.5;
else
return 8.0;
}


折扣计算:

float CMember::GetRebate()
{
if(m_nPoint<=100)
return 1.0;
else if(m_nPoint>100 && m_nPoint <200)
return 0.95;
else if(m_nPoint>=200 && m_nPoint <400)
return 0.9;
else if(m_nPoint>=400 && m_nPoint <600)
return 0.85;
else
return 0.8;
}

#10楼    回复  引用    

2006-03-13 13:46 by 史上最菜鸟 [未注册用户]
看了 ^.^的:
“一共是126元。”
顾客递给营业员130元。
营业员:“收你130元,找你3元。”

-----呵呵比较幽默营业员得到小费一元.

再看楼主改的:
顾客: “你算错了吧?”
营业员:“噢,不好意思,再给你1元。”(肯定在想哪件漂亮的衣服)

真是太幽默了~~

#11楼    回复  引用    

2006-03-13 14:37 by 皎 [未注册用户]
营业员:“噢,不好意思,再给你1元。”(肯定在想哪件漂亮的衣服)
---------------------------
肯定是在想今晚跟谁一块去浪漫啊..呵呵..

楼主是用VC++来编写程序的?

#12楼 [楼主]   回复  引用  查看    

2006-03-13 15:36 by 源码工作室      
是的,不瞒大家说,我没有在.net上写过程序。
所以我只能用我最擅长的。

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2006-03-09 16:39 编辑过