源码工作室

目标:通俗的语言说出通俗的技术
  博客园  :: 首页  :: 新随笔  :: 联系 :: 管理

书店会员销售系统(三)

Posted on 2006-03-14 11:10  源码工作室  阅读(4277)  评论(4编辑  收藏  举报
书店会员销售系统(三)
                     ――OORefactoring and Design Pattern
本节目的:
1.         学习使用Abstract Factory模式。
2.         学习使用Factory Method模式。
3.         使用重构手法。

客户:  “你们前期的工作很不错,我们非常满意。”
项目经理:“谢谢你们的肯定。”
客户:   “不过打印的功能我想下个版本应该做好了吧?”
项目经理:“你们的打印是不是要支持屏幕打印和纸张打印两种模式?”
客户:    “Very Good!我就想这样做,你刚才不提醒我,我可能还忘了呢。”
项目经理:“好的,下个版本我们会实现这个功能的。”
    等客户走后,项目经理的脸沉了下来:“狡猾狡猾的,先表扬了一下,我看就没有好心肠,这不,活又来了。”然后径直向程序员走去。

   
客户就是上帝,这点我们不容怀疑。
    最苦的还是程序员,这点我们也不容怀疑。
    先来分析一下,这里有两种打印方式:POS机屏幕打印,纸张打印,理所当然要建一个抽象类,然后建两个子类。
    为了方便,我们这里只打印累计点数和金额。

   
    在main函数中加入以下代码:

    CPrint *pPrint =  new CScreenPrint;
    
bool bRel = pPrint->Print(pMember->GetPoint(),fConsumeSum);
    assert(bRel);

    delete pPrint;
    pPrint 
= new CPaperPrint;
    bRel 
= pPrint->Print(pMember->GetPoint(),fConsumeSum);
    assert(bRel);
    delete pPrint;

    CPrint类代码如下:

class CPrint  
{
public:
    
virtual ~CPrint();
    
virtual bool Print(int nPoint,float fSum) = 0;
}
;

    CScreenPrint类代码如下:

class CScreenPrint : public CPrint  
{
public:
    CScreenPrint();
    
~CScreenPrint();
     
bool Print(int nPoint,float fSum);
}
;

bool CScreenPrint::Print(int nPoint,float fSum)
{
    printf(
"***Print on Screen:Point = %d, ConsumeSum =%f***\n",nPoint,fSum);
    
return TRUE;
}


    CPaperPrint类代码如下:

class CPaperPrint : public CPrint  
{
public:
    CPaperPrint();
    
~CPaperPrint();
    
bool Print(int nPoint,float fSum);
}


bool CPaperPrint::Print(int nPoint,float fSum)
{
    printf(
"$$$Print on Paper:Point = %d, ConsumeSum =%f$$$\n",nPoint,fSum);
    
return TRUE;
}


    编译通过,测试用例通过。
    程序运行的很好,没有新的需求,我们就让它这样吧,何必自寻烦恼呢?
    然而天下哪有这么好的事呢,过不了多久,客户就开始抱怨了:“我有两个分店,我想每个分店的打印格式不同。”
    不可否认,这确实是一个合理的要求,那就做吧,开始重构,使新功能更容易加入。
    首先进行改名,UML图如下:

   

    修改CScreenPrintShop1::Print函数为:

bool CScreenPrintShop1::Print(int nPoint,float fSum)
{
    printf(
"***Shop 1 Print on Screen:Point = %d, ConsumeSum =%f***\n",nPoint,fSum);
    
return TRUE;
}

修改CPaperPrintShop1::Print函数为:

bool CPaperPrintShop1::Print(int nPoint,float fSum)
{
    printf(
"$$$Shop 1 Print on Paper:Point = %d, ConsumeSum =%f$$$\n",nPoint,fSum);
    
return TRUE;
}

    当然main函数中也要做相应地改动。
    编译通过,测试用例通过。
    现在可以增加shop 2了,UML图如下:

    
    代码我就不再贴了,和shop1差不多。把出现调用CPaperPrintShop1、CScreenPrintShop1的地方都修改为Shop2,编译通过,测试用例通过。
    马上有朋友会问了,这不是一个很明确的Factory Method设计模式吗?是的,这是Factory Method设计模式的应用,但我们只有在一处调用,何必麻烦去写Factory类呢?毕竟使用一些模式会增加好多的代码。
    记住一点:不一定用上设计模式才是完美的,实用才是硬道理。
    但正如你所说,随着需求的变化,程序中有两处或两处以上用到了打印的功能,那我们就必须修改了。UML图如下:

   

    不慌写代码,这里我们还能分析一下,Screen Print和Paper Print都会用到,为何我们不合并一下呢?UML图如下:

   

    这是一个什么模式啊?其实这是Abstract Factory的简化版。
    现在在一开始就初始化打印:

    CPrintFactory*  pPrintFactory = new CShop1PrintFactory;

    然后在调用的地方写如下代码:

    CPrint *pPrint =  pPrintFactory->CreateScreenPrint();
    
bool bRel = pPrint->Print(pMember->GetPoint(),fConsumeSum);
    assert(bRel);

    delete pPrint;
    pPrint 
= pPrintFactory->CreatePaperPrint();
    bRel 
= pPrint->Print(pMember->GetPoint(),fConsumeSum);
    assert(bRel);
    delete pPrint;

    开始写代码吧,用在写代码的时间其实是比较少的,大多数的时间都是在设计和调试上,而调试的时间又占了大部分,我们要增加编程的效率,缩短调试的时间是最有效的,而建立测试用例就是一个很有效的方法。
    使用C++语言编写,在VC++ 6.0环境调试通过。
    代码下载
参考资料:
 Refactoring: Improving the Design of Existing Code》 ――Martin Fowler
 Design Patterns - Elements of Reusable Object-Oriented Software》 ――GoF