伍迷家园

让编程融入生活
随笔 - 82, 文章 - 0, 评论 - 1516, 引用 - 160
数据加载中……

小菜编程成长记(八 用“策略模式”是一种好策略)

 (续上篇)
       小菜次日来找大鸟,说:“《深入浅出设计模式》的第一章我看完了,它讲的是策略模式(Strategy)。『策略模式』定义了算法家族,分别封装起来,让它们之间可以互相替换, 此模式让算法的变化, 不会影响到使用算法的客户。看来商场收银系统应该考虑用策略模式?”
      “你问我?你说呢?”大鸟笑道,“商场收银时如何促销,用打折还是返利,其实都是一些算法,用工厂来生成算法对象,感觉是不是很怪?而最重要的是这些算法是随时都可能互相替换的,这就是变化点,而封装变化点是我们面向对象的一种很重要的思维方式。”

       策略模式的结构 (源自吕震宇 博客)
       


      这个模式涉及到三个角色:

 

  • 环境(Context)角色:持有一个Strategy类的引用。
  • 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
  • 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

      “我明白了,”小菜说,“我昨天写的CashSuper就是抽象策略,而正常收费CashNormal、打折收费CashRebate和返利收费CashReturn就是三个具体策略,也就是策略模式中说的具体算法,对吧?”


      “是的,那么关键就在于Context以及客户端程序如何写了?去查查资料,研究后把代码写出来给我看。”大鸟鼓励道。
      “好的,我一定很快写出来给你看!”小菜很兴奋。

        过一小时后,小菜给出商场收银程序的第四份作业。
CashContext类代码如下:

 

    
    
//收费策略Context
    class CashContext
    
{
        
//声明一个现金收费父类对象
        private CashSuper cs;

        
//设置策略行为,参数为具体的现金收费子类(正常,打折或返利)
        public void setBehavior(CashSuper csuper)
        
{
            
this.cs = csuper;
        }


        
//得到现金促销计算结果(利用了多态机制,不同的策略行为导致不同的结果)
        public double GetResult(double money)
        
{
            
return cs.acceptCash(money);
        }

    }

客户端主要代码如下:


       
double total = 0.0d;//用于总计
        private void btnOk_Click(object sender, EventArgs e)
        
{
            CashContext cc 
= new CashContext();
            
switch (cbxType.SelectedItem.ToString())
            
{
                
case "正常收费":
                    cc.setBehavior(
new CashNormal());
                    
break;
                
case "满300返100":
                    cc.setBehavior(
new CashReturn("300","100"));
                    
break;
                
case "打8折":
                    cc.setBehavior(
new CashRebate("0.8"));
                    
break;
            }

            
            
double totalPrices = 0d;
            totalPrices 
= cc.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));
            total 
= total + totalPrices;
            lbxList.Items.Add(
"单价:" + txtPrice.Text + " 数量:" + txtNum.Text + " "+cbxType.SelectedItem+ " 合计:" + totalPrices.ToString());
            lblResult.Text 
= total.ToString();
        }

实现的界面同之前一样(可点击使用)


       “大鸟,我用策略模式是实现了,但有些疑问,用了策略模式,则把分支判断又放回到客户端来了,这等于要改变需求算法时,还是要去更改客户端的程序呀?”
       “问得好,如果不是因为前面有工厂的例子,再来通过你的思考写出的这个策略模式的程序,你就问不出这样的问题的。”大鸟很开心,继续讲道,“最初的策略模式是有缺点的,客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。”
        “那还不如工厂模式好用,至少要增加促销或改进打折额度时,不用去大改界面,而现在,界面程序要承担的责任还是太大。没有体现你说的封装变化点的作用呀。”小菜疑问多多。
       “就目前而言,的确是这样,这样的程序确实还是不够完善,要改的地方还很多。”大鸟说道,“不过正所谓病毒时间长了会有变种,杀毒软件本身也会随着病毒的变化而升级改良,如果我们对策略模式做一些改进,引入一些新的技术处理方式,就可以避免现在的这种耦合了。小菜,又有新的东西要学了,好好加油呀!”
      “大鸟,谢谢你,,你总是让我带着问题去思考,而不是直接说答案,我觉得这样学习进步很快,也不觉得设计模式很难了。” 
      “,用不着这么客气,我只是觉得,没有人是天生就牛X的,有一些所谓的技术牛人总会在人面前说什么,‘你连这都不懂’,‘这还不简单了,你够笨的’等等说词。给人感觉他非常聪明,天生就会的样子,其实他在之前也不知走过多少弯路,犯过多少错,或许他之前也被更早的牛人羞辱过,所以再继续把羞辱传给后人。”大鸟有些激动。
       小菜小心的说道:“大鸟,你是不是也曾经被人羞……”
       “哈哈,马云曾说过,男人的胸怀是被冤枉撑大的!天天在这行当里混,阅人无数,被羞辱也是正常的事了。问题在于是不是头脑清醒,自己不能放弃呀。所以我希望能真正的帮助初学者成长,而不是去显示牛气充当狂人。小菜,记住,学习一定是一个自己感悟的过程,而程序员的感悟就是自己写程序做项目,通过实践再学习,最终升华为牛人。
      “嗯,我记住了,不过到底如何改良策略模式呢?”
       大鸟微笑不语

(待续)
本文相关源代码
       

posted on 2007-03-21 15:40 伍迷 阅读(6115) 评论(20)  编辑 收藏 所属分类: 面向对象小菜编程成长记

评论

#1楼    回复  引用  查看    

想看看结果,不要吊人胃口了。
2007-03-21 17:07 | 航天奇侠      

#2楼    回复  引用    

好文章,我也想想该如何改良策略模式。
2007-03-21 18:13 | lfzx_1227 [未注册用户]

#3楼    回复  引用    

好文章。
2007-03-21 19:20 | sina [未注册用户]

#4楼    回复  引用  查看    

是不是可以将策略的获取用工厂模式来获得?
2007-03-21 20:48 | 西门子乌      

#5楼    回复  引用  查看    

很棒!
繼續期待下文中...
2007-03-22 08:18 | 小哈      

#6楼    回复  引用  查看    


小菜编程成长记 之六 ?
2007-03-22 10:45 | gmsft      

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

@gmsft
六在博客中有,不过因为和设计模式不太相关,所以没放入《面向对象》分类中。
2007-03-22 12:23 | 伍迷      

#8楼    回复  引用    

男人的胸怀是被冤枉撑大的!天天在这行当里混,阅人无数,被羞辱也是正常的事了。问题在于是不是头脑清醒,自己不能放弃呀。所以我希望能真正的帮助初学者成长,而不是去显示牛气充当狂人。小菜,记住,学习一定是一个自己感悟的过程,而程序员的感悟就是自己写程序做项目,通过实践再学习,最终升华为牛人。”
2007-03-29 12:29 | wuyisky [未注册用户]

#9楼    回复  引用    

偶是小菜,生活中像大鸟这样的前辈还是很少的,
2007-12-15 16:45 | 小侯 [未注册用户]

#10楼    回复  引用    

To 作者:

在这里,似乎和作者对策略模式的理解有所不同。

"环境(Context)角色:持有一个Strategy类的引用。"

这个解释无法突出UML图里的聚合现象.

具体请看本人论坛的帖子: http://www.r-city.net/index.php?topic=216.msg637#msg637
欢迎来砸场子.
2008-01-23 21:43 | yarco [未注册用户]

#11楼    回复  引用    

另外再次感谢作者,实际上我能很明确的看到UML图利的这个聚合现象,完全是因为您在本书里放的大雁和雁群的关系.
我几乎在一霎那间就懂了UML :)
2008-01-23 21:59 | yarco [未注册用户]

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

@yarco
感谢您的认真阅读,并写了非常详细的阅读感受。我觉得您的这种学习态度是非常值得肯定的,相信您今后一定在技术上会更上一层楼。

我认真的阅读了您的分析,应该说,您的分析是很有道理的。我就对您的一些质疑做些解释。

1、关于第一章的跨越过大,这的确是写书时考虑过的,所以在书后,增加了“附录一 面向对象基础”一章,但是在第一章中却没有强调(只在前言中强调力度不够)。我已经准备在重印时,在第一章的1.10前,增加

(作者注:以上代码读者如果看着吃力,说明您对继承、多态、虚方法、方法重写等概念的理解不够,建议先阅读本书附录一,理解这些基本概念后再继续往下阅读。)

这段文字,这样就不至于让完全不理解面向对象的朋友产生阅读障碍。

2、关于“如果商场现在需要拆迁,没办法,只能跳楼价销售,商场的所有商品都需要打8折,打折后的价钱再每种商品满300送50,最后计总价的时候,商场还满1000送200,你说如何办?”这段话的解决问题。

在《小菜编程成长记》中是没有给出好的答案的,也就是说,网页上文章没有解释清楚“策略模式”的关键。
而《大话》书中,其实也没有对这个问题,即两个策略的组合问题做进一步的讲述,这点的确是我的遗憾,不过书中对简单工厂和策略模式做了分析和改进,应该还是值得一读。
严格来说,两个策略的组合问题,我现在的理解应该是用装饰模式来解决会更加好,书中却没有给出答案,具体如何做就留给读者自己去思考了。

再次感谢您的回复。大家一同努力
2008-01-23 22:45 | 伍迷      

#13楼    回复  引用    

看了工厂模式和策略模式感觉都非常类似
感觉工厂模式是属于创建模式
而策略模式是属于行为模式
其他的区别好像没看出来哦
能总结下么
最好讲到类似的设计模式的时候能来一个对比说明
这样读者会更加明白其各自的应用场合
2008-03-26 17:32 | wangmengfei [未注册用户]

#14楼    回复  引用    

寓教于乐!
2008-05-23 09:26 | 念时回复 [未注册用户]

#15楼    回复  引用  查看    

博客内容和书中的内容相比有较大变化
2008-08-03 19:58 | 天堂明月      

#16楼    回复  引用  查看    

写得很好。学习了。
2008-08-05 16:38 | 寻梦E.net      

#17楼    回复  引用  查看    

我觉得,这一系列的blog不仅仅讲博客,还讲了很多道理,我又有了新的勇气。
2008-08-20 00:11 | RainySummer      

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-03-22 18:14 编辑过
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接: