书店会员销售系统(二)

书店会员销售系统(二)
                     ――OORefactoring and Design Pattern
本节目的:
1.         学习使用策略模式。
2.         使用重构手法。

客户:  “我觉得你们的打折算法有点问题。”
项目经理:“有什么问题?
客户:   “在这种算法中,顾客要消费至少1000元才能享受折扣优惠,我想根据书店的规模,采取不同的折扣   算法,这就需要让我能设置折扣算法。”
项目经理:“你说得比较有道理,我们会修改程序,满足你的要求。”
客户:    “那下个版本给我实现这个功能吧?”
项目经理:“没有问题。”

     听到这段对话,我们可以知道下一步我们需添加的新功能是配置折扣算法,但在实现之前,让我们先戴上“重构”的帽子。
    在CMember会员类中,根据累计点数计算折扣这个功能有很不稳定的因素,我们应该把它提炼到一个类里去。
class CRebateRule 
{
public:
    CRebateRule();
    ~CRebateRule();
    float CalcRebate(int nPoint);

}; 

float CRebateRule::CalcRebate(int nPoint)
{
    if(nPoint<=100)
        return 10;
    else if(nPoint>100 && nPoint <200)
        return 9.5;
    else if(nPoint>=200 && nPoint <400)
        return 9;
    else if(nPoint>=400 && nPoint <600)
        return 8.5;
    else
        return 8.0;
}
    然后要对CMember::GetRebate()进行修改。
float CMember::GetRebate()
{
    CRebateRule RebateRule;
    return RebateRule.CalcRebate(m_nPoint);
}
    编译程序,通过测试。
    客户不是要配置自己的打折算法吗?我们可以把打折算法写在配置文件中,然后读取到数据结构中,这样就比较灵活了。现在是脱下“重构”的帽子,戴上“添加新功能”的帽子了。
    为CRebateRule类添加一函数。
bool  CRebateRule::Init(char *pszConfigFilename)
{
    //从配置文件中读取打折算法,在这里我就不多写了。
    return true;
} 

float CRebateRule::CalcRebate(int nPoint)
{
    //为了方便,就假设这是从配置文件中读取的。
    if(nPoint<=100)
        return 10;
    else if(nPoint>100 && nPoint <200)
        return 9.5;
    else if(nPoint>=200 && nPoint <400)
        return 9;
    else if(nPoint>=400 && nPoint <600)
        return 8.5;
    else
        return 8.0;
}

    修改CMember::GetRebate()函数:
float CMember::GetRebate()
{
    CRebateRule RebateRule;
    RebateRule.Init("Configfile.txt");
    return RebateRule.CalcRebate(m_nPoint);
}
    编译程序,通过测试。
    然而过了一段时间,客户又开始抱怨了:“我想在一些节假日实现折上加折的功能,现在的程序还不能实现。”
    到现在,策略模式就该上场了,然而在加新功能之前,我们还是得考虑一下,问自己一个问题:“现在我加上这个功能,需要改动原有类的一些代码吗?”回答是肯定的。那不妨我们再戴上“重构”的帽子。
    仔细观察CCalculate类,其中的两个函数,一个是累计会员的点数,一个是根据会员享受的折扣来计算消费的金额,我想我们可以把他们移到CMember类中去会更好些。
    先修改main函数:
int main(int argc, char* argv[])
{
    char    szMemberID[MAX_PATH];
    strcpy(szMemberID,"00000001");
    float fConsumeSum = 120.0;
    int   nPoint    = 0; 

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

    assert(nPoint == 87);
    assert(fConsumeSum == 120.0); 

    printf("Point = %d/n",nPoint);
    printf("ConsumeSum = %f/n",fConsumeSum); 

    delete pMember;
    return 0;
}

CMember中加了两函数:
int CMember::CalculatePoint(float fSum)
{
    int nPoint = (int)(fSum/10);
    return nPoint;
}

 float CMember::CalcMoney(float fSum)
{
    float fRebateSum = 0.0;
    fRebateSum = fSum * GetRebate()/10;
    return fRebateSum;
}
    编译程序,通过测试。
    让我们再来分析一下,CalculatePoint函数是否要向外公开了,我们完全可以在计算折扣金额的时候一起累计点数。
    所以main函数我们又可以修改了。
int main(int argc, char* argv[])
{
    char    szMemberID[MAX_PATH];
    strcpy(szMemberID,"00000001");
    float fConsumeSum = 120.0; 

    CMember     *pMember = new CMember(szMemberID,1000.0,75);
    fConsumeSum = pMember->CalcMoney(fConsumeSum);
    assert(pMember->GetPoint() == 87);
    assert(fConsumeSum == 120.0); 

    printf("Point = %d/n",nPoint);
    printf("ConsumeSum = %f/n",fConsumeSum); 

    delete pMember;
    return 0;
}
修改CMember::CalculatePoint为私有函数。
修改CMember::CalcMoney函数。
float CMember::CalcMoney(float fSum)
{
    float fRebateSum = 0.0;
    fRebateSum = fSum * GetRebate()/10;
    CalculatePoint(fSum);
    return fRebateSum;
}
    编译程序通过,可调试不通过。简单的查了下原因,原来是CMember::CalculatePoint还没有修改正确。
    修改后:
int CMember::CalculatePoint(float fSum)
{
    m_nPoint = m_nPoint + (int)(fSum/10);
    return m_nPoint;
}
    编译程序,通过测试。
    CMember::CalculatePoint还需要返回参数吗?改成void的吧。别忘了编译测试,保证每次的一小步,我们也能走到“黄河”。
    再思考一下,CalcMoney这个名字适合吗?RebateSumAndCumulatePoint可能会更好点。
    以上的重构都不是为了添加新功能,只是为了程序更简洁、更具可读性。下面的重构才开始为了添加新功能而做的。UML图如下:


    把现有的CRebateRule改名为CNormalRebateRule,然后修改程序让其通过编译测试。再创建一个新的抽象类CRebateRule。
class CRebateRule 
{
public:
    virtual ~CRebateRule();
    virtual bool  Init(char *pszConfigFilename) = 0;
    virtual float CalcRebate(int nPoint) = 0;
};
    根据设计,我们继续修改程序,使它通过编译测试。到现在为止,我们可以加新功能了。这样我们添加CSpecialRebateRule类,现有的类都不需要修改了,戴上“添加新功能”的帽子吧。
    我们先规定折上在打9折。先写main函数:
int main(int argc, char* argv[])
{
    char    szMemberID[MAX_PATH];
    strcpy(szMemberID,"00000001");
    float fConsumeSum = 120.0; 

    CMember     *pMember = new CMember(szMemberID,1000.0,75);
    CRebateRule *pRebateRule = new CNormalRebateRule;
    pRebateRule->Init("Configfile.txt"); 
    fConsumeSum = pMember->RebateSumAndCumulatePoint(fConsumeSum,pRebateRule);
    assert(pMember->GetPoint() == 87);
    assert(fConsumeSum == 120.0);
    printf("Point = %d/n",pMember->GetPoint());
    printf("ConsumeSum = %f/n",fConsumeSum); 

    delete pRebateRule;
    pRebateRule = new CSpecialRebateRule;
    pRebateRule->Init("Configfile.txt");    
    fConsumeSum = pMember->RebateSumAndCumulatePoint(300.0,pRebateRule);
    assert(pMember->GetPoint() == 117);
    assert(fConsumeSum == 270.0);
    printf("Point = %d/n",pMember->GetPoint());
    printf("ConsumeSum = %f/n",fConsumeSum);   

    delete pMember;
    delete pRebateRule;
    return 0;
}
    增加CSpecialRebateRule类及函数,这里类的代码我就不写出来了,可以下载代码。
float CSpecialRebateRule::CalcRebate(int nPoint)
{
    //假设现在用户已经享受9.5折的优惠。
    return (9.5*9.0/10);
}
    编译程序,通过测试。
    也许有人会问,这里为何不用Factory Method设计模式呢?在这里折扣算法类只有CMember类使用,而不是像Log类样到处使用的,毕竟使用Factory Method还会引入Factory类,会增加程序的复杂度,在不必要用的时候,我们还是避免使用,简单的可以使用Simple Factory来替代。
    记住一点:不要一开始就添加新功能,而是通过重构的手法,不改变软件的原来功能,逐步地修改程序,使其适合新功能的加入。
    好了,说的也比较罗嗦了,但这是一个编程的过程。
    使用C++语言编写,在VC++ 6.0环境调试通过。
    下载代码
 

参考资料:
 Refactoring: Improving the Design of Existing Code》 ――Martin Fowler
 《Design Patterns - Elements of Reusable Object-Oriented Software》 ――GoF

http://goodcandle.cnblogs.com/archive/2006/03/10/booksell2.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二章可行性分析 通过对一些典型书店、图书馆,并结合企业要求开发的一套信息化管理系统。 本系统的实现目标是 (1)为工作人员提供一个工作平台:员工可在网上完成日常事务,实现无纸化办公。即职工通过计算机完成采集信息,处理信息,分析信息等工作。 (2)为管理者提供一个控制平台:控制平台就是管理者能通过业务控制平台,把企业的各项制度、标准,通过程序控制落实到企业各项工作活动中。通过对工作流进行设置与监控,从而能严格控制企业活动的各项动作,实现事务的有效管理。 (3)为系统维护者提供一个集中维护的平台:系统管理人员能对系统运行的缺陷、故障进行集中处理,使系统管理人员能够快速、有效、连续的对系统进行维护与调整。 第三章需求分析 3.1系统总体的功能需求 系统在界面设计方面要尽可能的人性化,对用户使用而言应该是简单易用的,在布局和设计上要科学化。 就目前而言,该系统是为中小型书店研发的。系统开发的目标是实现书店图书租赁的系统化、规范化和自动化,这是在用户要求的基础上提出来的,功能要求如下: 1、管理员能对书店租赁系统里的会员信息、图书信息、借阅信息、收入信息等进行数据的添加、修改、删除、查询以及统计的功能操作。 2、会员能够登录系统和修改密码,并且只能够查询自己的借阅信息和个人信息查询以及查询书店书库信息(包括书名、作者、出版社、库存数量等)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值