First we try, then we trust

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  183 随笔 :: 111 文章 :: 2983 评论 :: 339 引用

话说《三国演义》中,周瑜与孙权定下计谋,以孙权之妹为诱饵,骗刘备过江到东吴招亲,想趁机杀害刘备,索回荆州。诸葛亮早识破了诡计,令武将赵云随同并护卫刘备前往,并给赵云了三个锦囊,嘱咐他依次执行即可。结果,赵云按照诸葛亮的锦囊妙计行事,不仅帮助刘备将孙权之妹孙尚香夫人迎娶回来,还得到孙权之母吴国太的欢心,陪同刘备夫妇回了荆州。《三国演义》这段记叙,为后世创造了“锦囊妙计”和“赔了夫人又折兵”两个成语。

今天,我就“锦囊妙计”来说说Command模式。从面向对象的设计角度来说,锦囊妙计有以下几个特色:

1、妙计被装入锦囊后都变得一般模样,没有任何区别。

2、锦囊妙计自己是无法实施的,必须由某个人依计行事。

3、实施者有权利选择执行命令或不执行命令,也可以选择在什么时候执行。当然,在我们的故事中,赵云在合适的时间执行了合适的妙计,便为我们留下了精彩的故事。

这个故事用代码书写出来如下:

using System;
using System.Collections;

abstract class JinNang
{
  
public string message = "";

  
public void Execute()
  
{
    Console.WriteLine(message);
  }

}


class JinNang1 : JinNang
{
  
public JinNang1()
  
{
    
this.message = "ToDo:让刘备拜见岳艾乔国老,并大造声势。";
  }

}


class JinNang2 : JinNang
{
  
public JinNang2()
  
{
    
this.message = "ToDo:假报刘备:曹操起兵五十万,杀奔荆州。";
  }

}


class JinNang3: JinNang   
{
  
public JinNang3()
  
{
    
this.message = "ToDo:向孙夫人揭穿了孙权和周瑜的阴谋,请求夫人保护。";
  }

}


abstract class Receiver
{
  
protected Queue commands = new Queue();
  
  
public void SetCommand(JinNang c)
  
{
    commands.Enqueue(c);
  }


  
protected JinNang GetCommand()
  
{
    
if (this.commands.Count != 0)
    
{
      JinNang c 
= (JinNang)this.commands.Dequeue();
      
return c;
    }

    
else
      
return null;
  }


  
public bool FulfilCommand(JinNang c)
  
{
    
if(c != null)
    
{
      c.Execute();
      
return  true;
    }

    
else
    
{
      Console.WriteLine(
"惨了!无计可施了!");
      
return false;
    }

  }


  
abstract public void DoComplexJob();
}


class Zhaoyun : Receiver
{
  
public override void DoComplexJob()
  
{
    Console.WriteLine(
"护送刘备去东吴");
    Console.WriteLine(
"到达市徐");
    
if(!FulfilCommand(GetCommand())) return;

    Console.WriteLine(
"刘备娶了孙权之妹");
    Console.WriteLine(
"发现刘备全不想回荆州");
    
if(!FulfilCommand(GetCommand())) return;

    Console.WriteLine(
"被孙权追杀");
    
if(!FulfilCommand(GetCommand())) return;

    Console.WriteLine(
"安然护送刘备抵达荆州,任务完成!");
  }

}


class ZhuGeLiang
{
  
public void JinNangMiaoJi(Receiver r)
  
{
    r.SetCommand(
new JinNang1());
    r.SetCommand(
new JinNang2());
    r.SetCommand(
new JinNang3());
  }

}


public class Client
{
  
public static void Main( string[] args )
  
{
    Receiver r 
= new Zhaoyun();
    ZhuGeLiang z 
= new ZhuGeLiang();
    
    z.JinNangMiaoJi(r);
    r.DoComplexJob();
  }

}

“命令模式是对命令的封装。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象”。通过将妙计封装成锦囊妙计,便成为可委派的妙计。由一个对象(ZhuGeLiang)创建并封装,然后传递给另一个对象(ZhaoYun),命令在此没有被立即执行,而是Enqueue了。当条件合适的时候,再将命令解开执行。另外,所有的锦囊妙计都有一个共同的特性,就是可以被执行。所以,赵云不用关心这个锦囊与那个锦囊有什么区别,只需执行便是了。

该例子仅仅取了一个Command模式的“意”,丢弃了Command模式的形。设计模式的应用无需过于死板。如果对Command模式感兴趣,可以访问《设计模式(18)-Command Pattern》

posted on 2004-12-08 15:32 吕震宇 阅读(4982) 评论(28)  编辑 收藏 所属分类: 设计模式随笔系列

评论

#1楼  2004-12-08 15:52 wayfarer      
确实非同凡响,把设计模式与三国联系起来了。震宇兄可以考虑写一个《大话三国——设计模式》。

因为设计模式整体来说比较抽象,而采用大家耳熟能详的故事来描述设计模式,更容易理解。取其神,而去其形,对模式的应用就活了起来,确实是一种非常好的实践。
  回复  引用  查看    

#2楼  2004-12-08 16:21 wayfarer      
在Receiver类中,另外提供一个protected method:GetCommond(),可能比较好一点,至少从重构的角度来看是如此。

另外,有了这个方法的时候,在对队列作Dequeue()操作的时候,才可以加入一些异常判断。因为队列可能存在空。

protected JinNang GetCommand()
{
try
{
JinNang c = (JinNang)this.Commands.DeQueue();
return c;
}
catch
{
throw new Exception("The Command Queue is Empty.");

}
}
  回复  引用  查看    

#3楼  2004-12-08 17:33 wayfarer      
我把代码调试了一下,发现如果诸葛亮少放一条妙计,就会抛出队列为空的异常。也许我所说的GetCommand方法也不好,同样也是抛出异常,不如返回空值。然后在DoComplexJob()方法中,对返回值加上条件判断,比较好。

当然,这些更改与本身的模式设计无关,我只是从代码自身的角度来考虑。
protected JinNang GetCommand()
{
if (this.commands.Count != 0)
{
JinNang c = (JinNang)this.commands.Dequeue();
return c;
}
else
{
return null;
}
}

然后将原来的
c = (JinNang)this.commands.Dequeue();
改为:
JinNang c = (JinNang)this.GetCommand();
if (c != null)
{
FulfilCommand(c);
}

其实上面这一段又可以写为一个方法,因为我闻到重复代码的味道了。

呵呵,有些吹毛求疵。
  回复  引用  查看    

#4楼  2004-12-08 17:37 dljsoft      
真是不错,okay!
震宇,你对设计模式的理解已经达到化境了!

  回复  引用  查看    

#5楼 [楼主] 2004-12-08 17:53 吕震宇      
谢谢wayfarer灵敏的鼻子,敏锐的嗅到了代码腐化的味道。我已经按照重构的思想修改过来了。呵呵,谢谢。
  回复  引用  查看    

#6楼  2004-12-08 18:29 dudu      
写得妙!
  回复  引用  查看    

#7楼  2004-12-09 13:56 wildfish [未注册用户]
有典故引入设计模式,的确是经典啊:)
希望能出更多类似的:)
  回复  引用    

#8楼  2004-12-09 22:48 dq_lu      
写的不错,更能深刻理解了
  回复  引用  查看    

#9楼  2004-12-23 13:09 idior      
赵云似乎在模式中的身份更应该是 involker

  回复  引用  查看    

吕兄一心想写出好的文章这个心情我很理解,但是我看过《Java与模式》以后,只能说你的文章不过尔尔,不要被楼上几个虚夸的晕了头

既然号称是自己的东西就要有自己的特色才行啊,希望以后的随笔真的是来自自己的灵感而不是看书的摘抄。

注:在《java与模式》中这个例子是用来表示策略模式的,虽然这里改为了命令模式(还有点牵强),但是仍然看得出是由此书而来。请吕兄在写类似的随笔的时候记得多上一句话:看。。。书时不禁深受启发,我们来把作者的思路扩展一下吧~~~

实乃忠告,非无理取闹也
  回复  引用    

#11楼 [楼主] 2004-12-24 09:30 吕震宇      
@Java与模式

1、不知道这位是不是阎宏,如果是的话就说出来并且证明给大家看。“Java与模式”的名字怎么给人一种狐假虎威的感觉?

2、任何善意的忠告我都会虚心接受。但是你的评论中一些结论性的东西让我对你的忠告表示怀疑(“只能说你的文章不过尔尔”,“希望以后的随笔真的是来自自己的灵感而不是看书的摘抄”...),下这个结论的人的心思恐怕另有它意吧?

3、记得一次我出去旅游,碰到一个学佛学的同伴,在参观过一个庙宇后他说,佛家做法事的时候经常要摆上几盆清水。如果你眼中只有清水的话,你就没有参透佛门“清净心”的本意。一盆清水便是一盆清水,有些人用它表示“清净心”,有些人却用它金盆洗手。你总不能跟和尚们说,这盆清水是金盆洗手的思路扩展吧?设计模式的关键是在其意。如果你觉得锦囊妙计只能表示策略模式的化,我觉得你的眼中还是只有一盆清水而已。
  回复  引用  查看    

哈哈,吕兄高抬我了,我只是读过两三本设计模式书的一凡夫。如果你觉得我的忠告不是善意的,那就当我没说。听说你是位老师,如果这样容不得批评,真不知道你怎么为人师表~~。我在上一片留言上已经说得很明白了:不是自己的就不要说是自己的,你的思路和创意始终没有超出一本《java与模式》,何以信誓旦旦的说了还不脸红?

做人都要讲一个良心,用了别人的,扩展了别人的这本没有错,但是错在抹去别人的名字贴上自己的招牌。当然这也是爱慕虚荣的表现。

吕兄好自为之
  回复  引用    

#13楼 [楼主] 2004-12-24 13:25 吕震宇      
@我不是阎宏

谢谢了,我会的。祝圣诞快乐!
  回复  引用  查看    

《Java与模式》639页倒数第二行:“因此在历史上同样叫做“锦囊妙计”的故事,可能属于不同的模式。命令模式更接近这个历史故事所描述的,...”(注:故事是诸葛亮临死授计杀魏延)
  回复  引用    

#15楼  2004-12-25 14:26 wayfarer      
@《Java与模式》还是我不是阎宏?
不管你是谁,言谈之中很推崇《Java与模式》,我想这没有错。《Java与模式》确实也是一本好书,但我从你老兄的几句话,看出来的意见有失偏颇。
“吕兄一心想写出好的文章这个心情我很理解,但是我看过《Java与模式》以后,只能说你的文章不过尔尔”。
按你的逻辑来判断,那么我可以说:“某人想写出好书的心情我理解,但是我看过GOF的《设计模式》以后,只能说某人的书不过尔尔”。

评价一个人的东西,总要摸着良心来。即使看过《Java与模式》n遍,对其推崇备至的人,不见得能写出如本文一样的好文章来。何况吕震宇现在写的是随笔,是在博客上发表,没有必要如论文一样列出参考文献吧。且作者本人并没有标榜自己是如何如何的优秀,倒是你却定要为作者扣上一顶帽子,不能不让人怀疑你的用意了。

“不是自己的就不要说是自己的,你的思路和创意始终没有超出一本《java与模式》,何以信誓旦旦的说了还不脸红?”哈,我倒想说,究竟这个世界是否是你一个人说了算。如真是这样,我无语了。没有任何根据,就在下结论,只能证明你的结论是草率的。

如果因为《java与模式》用过“锦囊妙计”,其他人就不能用了,这未免太专制了吧。既然是这样,我想陈寿写了《三国志》后,为何罗贯中偏要来写一部《三国演义》。为什么GOF写了《设计模式》,阎宏还要来写这本《Java与模式》?

作者写这篇文章的目的是什么呢?难道是沽名钓誉么?老实说,发表在自己的博客上,又能沽什么名,钓什么誉?无非就是把自己的所感,自己的经验(不管是从项目中获得,还是从书中获得),写出来与大家分享而已。

如果有人能从文章中学到什么,则作者幸甚;如果有人能从文章中发现些错误,作者仍然幸甚!可我不明白的是,却总有些人,带着另一种眼光,来发表一些名虽忠告,实则是另有深意的评论。为什么?

只要是好的批评,我想吕震宇必有采纳的雅量。
  回复  引用  查看    

#16楼  2005-01-05 12:52 coffee [未注册用户]
@《Java与模式》还是我不是阎宏?

以后说话要注意些,无论是谁的东西,对大家有帮助的毕竟是好东西,吕老师写出的东西对大家都是有帮助的就足够了!
  回复  引用    

#17楼  2005-01-05 16:55 周奔驰      
支持吕震宇,就算一样又怎么样,思想是可以share的,那边谈Java,这边谈C#, 能够提炼出来对大家有帮助的就是好东西,希望你也可以抽空做做这些功德无量的事情,不要只会在一旁指手画脚。
  回复  引用  查看    

#18楼  2005-01-07 11:47 JustinLee      
支持震宇兄!


  回复  引用  查看    

#19楼 [楼主] 2005-01-07 20:56 吕震宇      
在这个例子中,我对Command模式的理解似乎出了些偏差。多亏idior指正。本文就不再进行修改了。也作为我对Command模式学习历程的一个见证。具体讨论可以参考《设计模式随笔-再论锦囊妙计》一文的idior的评论。

http://www2.cnblogs.com/zhenyulu/archive/2005/01/07/87237.html
  回复  引用  查看    

#20楼  2005-09-02 13:23 Unknown [未注册用户]
用新东方老罗的话讲:彪悍的人生不需要解释,人生中总有几次踩到大便的时候。

呵呵,吕兄,继续。。。
  回复  引用    

#21楼  2005-10-07 18:21 Lixx_XIT [未注册用户]
支持震宇老师。《Java与模式》我也看过,书写得不错。但震宇老师的随笔也很精炼,各有所长,更何况两者的目的根本不同。“我不是阎宏“”的说法有些偏激,希望震宇老师多出作品。
  回复  引用    

#22楼  2005-11-19 15:11 netx [未注册用户]
支持吕老师
强烈支持吕老师


  回复  引用    

#23楼  2005-11-27 00:15 sgb [未注册用户]
@《Java与模式》还是我不是阎宏

你能否举出这么生动的课件,让大家欣赏一下,好吗?
我想跟你讲的是:读书跟写书完全是两回事,即便是优秀的读者,也称不上作家。
强烈支持吕老师!
  回复  引用    

#24楼  2006-01-17 14:26 笑笑生 [未注册用户]

看过几个command模式。。但是每种设计都是列出队列中的所有命令并调用。。如果是由客户来决定调用其中的命令,应当如何去实现它?
  回复  引用    

#25楼  2007-06-18 18:07 丁梦野 [未注册用户]
你好强!
  回复  引用    

#26楼  2007-08-07 16:46 CY [未注册用户]
吕兄,支持你!
  回复  引用    

#27楼  2007-10-24 08:57 林林 [未注册用户]
TO@ 笑笑生
看过几个command模式。。但是每种设计都是列出队列中的所有命令并调用。。如果是由客户来决定调用其中的命令,应当如何去实现它?

我想哪应该是策略模式了
  回复  引用    

#28楼  2007-12-23 05:28 李华星      
每个人都有自己的想法, 做事方式也是一样, 不同的方式都可以达到同样的效果,那就是解决问题 无所谓谁对谁错的问题. 命令模式与策略模式确实用相似之处, 就看你怎么用 支持吕老师的观点
  回复  引用  查看    


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


相关链接: