设计模式—命令模式
设计模式—命令模式
我们首先想个电视机的例子,每个TV自己肯定实现了开机open,关机close,下一频道功能Next、上一频道Prev、选中某个频道SetTo等功能。TV类如下:
public class TV
{
/// <summary>
/// 开机
/// </summary>
public void Open()
{
}
/// <summary>
/// 关机
/// </summary>
public void Close()
{
}
/// <summary>
/// 下一频道
/// </summary>
public void Next()
{
}
/// <summary>
/// 上一频道
/// </summary>
public void Prev()
{
}
/// <summary>
/// 设置到某个频道
/// </summary>
/// <param name="channel"></param>
public void SetTo(Channel channel)
{
}
}
现在科技发展了,我们一般都能遥控了,所以我们要开发一个遥控器Control如下:
public class Controls
{
private TV tv;
public Controls(TV Tv)
{
tv=Tv;
}
public void Open()
{
ArrayList list = new ArrayList();
list.Add(new Open(tv));
list.Add(new Close(tv));
tv.Open();
}
public void Close()
{
tv.Close();
}
public void Next()
{
tv.Next();
}
public void Prev()
{
tv.Prev();
}
public void SetTo(Channel channel)
{
tv.SetTo(channel);
}
}
其中Control类实例化需要传入TV的实例,实际上就是模拟用户将遥控器对准A电视机,马上传入A电视机进去实例化Control类。
假如现在我们突然又有新的需求了,我们的遥控器需要提供一个智能键,用户提前预定一部分指令,然后铵这个按钮,就执行这一系列操作。比如用户设置:先开机、然后跳到1频道、然后关机。大家想现在我们应该怎么办?我们每个命令都是自己独立的一个函数,我们无法将这些函数记录下来,然后逐一调用这些函数啊?大家再想可能随后还有一系列新的命令出来,这些命令可能千奇百怪,都需要对几段函数进行操作,如果我们都混在Control类里面是不是很明显造成Control类的不断扩大和功能的越来越复杂?所以我们必须要将这些函数提高到类的层面上来,让这些函数可以自由变化和增加。大家注意啊,很多人在讲解这个模式的时候都提到这个模式是将函数层面的东西提高到类的层面,可是这种提高不是闲着无聊,不是为了凑一个命令模式出来才这么干的,是象现在这样为了扩展解决这一类问题而提高的。请认真揣摩我这句话,模式不是类图,而是思想。
言归正卷,为了能够记录下每一个命令,我们将命令封装,封装出同一个方法Execute,这样我们在遍历集合的时候就可以直接.Execute()来调用该命令了。
public abstract class Cmd
{
protected TV tv;
public Cmd(TV Tv)
{
tv=Tv;
}
public abstract void Execute();
}
注意:命令必须知道要操作的对象,这里我将TV做为构造函数的参数。
下面是Open命令的例子:
public class Open:Cmd
{
public Open(TV Tv):base(Tv)
{
}
public override void Execute()
{
tv.Open();
}
}
接下来我只给出这个特殊命令的代码,其他都一样。
public class MacCmd:Cmd
{
private ArrayList cmdlist;
public MacCmd(TV Tv,ArrayList Cmdlist):base(Tv)
{
cmdlist=Cmdlist;
}
public override void Execute()
{
foreach(Cmd cmd in cmdlist)
{
cmd.Execute();
}
}
}
注意:MacCmd只负责执行命令,至于命令的ArrayList是怎么出来的他是不管的,也就是说控制器上肯定有其他功能按键负责设置出那一串指令。我这里为了简单,就直接用ArrayList来代替了。
设置指令的伪代码如下:
ArrayList list = new ArrayList();
list.Add(new Open(tv));
list.Add(new SetTo1(tv));
list.Add(new Close(tv));
这里大家应该可以看出将函数提高到类级别的意义了。
public class Controls
{
public void MacCmd()
{
(new MacCmd(tv,cmdList)).Execute();
}
}
大家可以看到,正因为我们将命令从函数级别提高到类级别,所以我们可以方便的对命令进行各种操作,什么排队啊,撤销啊等等都很方便实现了。
命令模式:
命令模式的根本目的在于将“行为请求者”与“行为实现者”解耦。这句话可能不好理解,再简单点说就是下达命令的人只需要做2点:下达命令内容、指出是对谁下的命令就行了,至于命令怎么被实现的一概不管,对应到电视机的例子就是,用户将遥控器对准要控制的电视机——指出是对谁下的命令,然后按开机按钮——下达命令内容(开机),至于怎么开机的你不用管。
肯定有人要问为什么要解耦?对应到电视的例子就是一种你直接去电视机上下达命令开机、另一种就是你按一下遥控器开机,为什么解耦就是为什么你不跑到电视机上去操作而要用遥控器?这其实包含了2个层面的原因:一是下命令者可能到不了“行为实现者”跟前,这个容易理解,比如要透过网络等等。另一个就是在“行为实现者”执行的时候命令者可能已经不存在了,这个似乎有些不太好理解,举个不恰当的例子你刚按下遥控器的开机键就挂了,但是虽然你挂了,这个命令仍然要执行(当然我不知道什么原因必须执行,在现实中确实会有这种情况存在),也就是电视照样还是开机了。
我讲了这么多,你千万不要一高兴,回家后把你所有页面的button事件全换成Command模式,还洋洋得意觉得这个模式真好用,记着我一再强调的话:模式是一种思想,不是类!!先看看你要解决的问题是不是符合我上面说的命令模式的思想再做决定,不然你画得再像也不是命令模式。
补充:看到星星远好在TerryLee的留言,我想解释一下:
看过例子后,还是没有发现为什么要用命令模式。我在别的参考书是如此论述命令模式的。
客户程序在很多时候只关注程序代码段A,但由于要执行A,必须要进行一些操作B,C。为了解决这个问题,引入命令模式,把B,C封装成类D,以及D提供一个接口可以执行代码A。在常见程序代码,我们执行的GUI就有例子,用户按下按钮,按钮类处理相关事情,然后调用程序员的按钮处理代码。我认为这才是命令模式。
你说的这种情况的确是命令模式的思想,只是从不同的方向来理解而已,对应到电视的例子就是你只对电视机关机感兴趣,但是你不能直接走到电视机跟前,至于原因对应我上面提到的2个层面的原因,所以你必须执行遥控器的操作来实现你的关机。不知道我这样的解释你是否还有疑问?
BTW:这个网上画图太复杂了,所以我就懒得画类图,大家见谅奥
后期补充一点,如果看了上面的描述还不明白,后面是一个简单的理解方式:命令者模式说白了就是我们不希望让命令发起者和命令执行者在同一个类里面,比如button,我们button_Click事件的代码就不在button本身里面,为什么?因为只有将button(命令发起者)跟button_Click(命令执行者)分开,才可以互相独立发展,写button的负责button的样式,写button_Click的负责业务代码,button不用了解具体命令怎么执行的,button_Click也不用了解命令发起者的详细情况。
下一篇:命令模式补充