.NET设计模式(17):命令模式(Command Pattern)

命令模式(Command Pattern

——.NET设计模式系列之十七

TerryLee20067

概述

在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合[李建忠]。这就是本文要说的Command模式。

意图

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。[GOF 《设计模式》]

结构图

Command模式结构图如下:

1  Command模式结构图

生活中的例子

Command模式将一个请求封装为一个对象,从而使你可以使用不同的请求对客户进行参数化。用餐时的账单是Command模式的一个例子。服务员接受顾客的点单,把它记在账单上封装。这个点单被排队等待烹饪。注意这里的"账单"是不依赖于菜单的,它可以被不同的顾客使用,因此它可以添入不同的点单项目。

2  使用用餐例子的Command模式对象图

Command模式解说

在众多的设计模式中,Command模式是很简单也很优雅的一种设计模式。Command模式它封装的是命令,把命令发出者的责任和命令执行者的责任分开。我们知道,一个类是一组操作和相应的一些变量的集合,现在有这样一个类Document,如下:

3

示意性代码:

/// <summary>

/// 文档类

/// </summary>


public class Document

{
    
/// <summary>

    
/// 显示操作

    
/// </summary>


    
public void Display()

    
{
        Console.WriteLine(
"Display");
    }
 

    
/// <summary>

    
/// 撤销操作

    
/// </summary>


    
public void Undo()

    
{
        Console.WriteLine(
"Undo");
    }


    
/// <summary>

    
/// 恢复操作

    
/// </summary>


    
public void Redo()

    
{
        Console.WriteLine(
"Redo");
    }

}

一般情况下我们使用这个类的时候,都会这样去写:

class Program

{
    
static void Main(string[] args)

    
{
        Document doc 
= new Document();

        doc.Display();

        doc.Undo();

        doc.Redo();
    }

}

这样的使用本来是没有任何问题的,但是我们看到在这个特定的应用中,出现了Undo/Redo的操作,这时如果行为的请求者和行为的实现者之间还是呈现这样一种紧耦合,就不太合适了。可以看到,客户程序是依赖于具体Document的命令(方法)的,引入Command模式,需要对Document中的三个命令进行抽象,这是Command模式最有意思的地方,因为在我们看来Display()Undo()Redo()这三个方法都应该是Document所具有的,如果单独抽象出来成一个命令对象,那就是把函数层面的功能提到了类的层面,有点功能分解的味道,我觉得这正是Command模式解决这类问题的优雅之处,先对命令对象进行抽象:

4

示意性代码:

/// <summary>

/// 抽象命令

/// </summary>


public abstract class DocumentCommand

{
    Document _document;

    
public DocumentCommand(Document doc)

    
{
        
this._document = doc;
    }


    
/// <summary>

    
/// 执行

    
/// </summary>


    
public abstract void Execute();

}

其他的具体命令类都继承于该抽象类,如下:


5

示意性代码:

/// <summary>

/// 显示命令

/// </summary>


public class DisplayCommand : DocumentCommand

{
    
public DisplayCommand(Document doc)

        : 
base(doc)
    
{    

    }


    
public override void Execute()

    
{
        _document.Display();   
    }

}



/// <summary>

/// 撤销命令

/// </summary>


public class UndoCommand : DocumentCommand


    
public UndoCommand(Document doc)

        : 
base(doc)
    
{   

    }


    
public override void Execute()

    
{
        _document.Undo();   
    }

}



/// <summary>

/// 重做命令

/// </summary>


public class RedoCommand : DocumentCommand

{
    
public RedoCommand(Document doc)

        : 
base(doc)
    


    }


    
public override void Execute()

    
{
        _document.Redo();   
    }
 
}

现在还需要一个Invoker角色的类,这其实相当于一个中间角色,前面我曾经说过,使用这样的一个中间层也是我们经常使用的手法,即把AB的依赖转换为AC的依赖。如下:

6

示意性代码:

/// <summary>

/// Invoker角色

/// </summary>


public class DocumentInvoker

{
    DocumentCommand _discmd;

    DocumentCommand _undcmd;

    DocumentCommand _redcmd;

    
public DocumentInvoker(DocumentCommand discmd,DocumentCommand undcmd,DocumentCommand redcmd)
    
{

        
this._discmd = discmd;

        
this._undcmd = undcmd;

        
this._redcmd = redcmd;

    }


    
public void Display()

    
{
        _discmd.Execute();
    }


    
public void Undo()

    
{
        _undcmd.Execute();
    }


    
public void Redo()

    
{
        _redcmd.Execute();
    }

}

现在再来看客户程序的调用代码:

class Program

{
    
static void Main(string[] args)

    
{

        Document doc 
= new Document();


        DocumentCommand discmd 
= new DisplayCommand(doc);

        DocumentCommand undcmd 
= new UndoCommand(doc);

        DocumentCommand redcmd 
= new RedoCommand(doc);


        DocumentInvoker invoker 
= new DocumentInvoker(discmd,undcmd,redcmd);

        invoker.Display();

        invoker.Undo();

        invoker.Redo();

    }

}

可以看到:

1.在客户程序中,不再依赖于DocumentDisplay()Undo()Redo()命令,通过Command对这些命令进行了封装,使用它的一个关键就是抽象的Command类,它定义了一个操作的接口。同时我们也可以看到,本来这三个命令仅仅是三个方法而已,但是通过Command模式却把它们提到了类的层面,这其实是违背了面向对象的原则,但它却优雅的解决了分离命令的请求者和命令的执行者的问题,在使用Command模式的时候,一定要判断好使用它的时机。

2.上面的Undo/Redo只是简单示意性的实现,如果要实现这样的效果,需要对命令对象设置一个状态,由命令对象可以把状态存储起来。

.NET中的Command模式

ASP.NETMVC模式中,有一种叫Front Controller的模式,它分为HandlerCommand树两个部分,Handler处理所有公共的逻辑,接收HTTP PostGet请求以及相关的参数并根据输入的参数选择正确的命令对象,然后将控制权传递到Command对象,由其完成后面的操作,这里面其实就是用到了Command模式。

7  Front Controller 的处理程序部分结构图

8 Front Controller的命令部分结构图

Handler 类负责处理各个 Web 请求,并将确定正确的 Command 对象这一职责委派给 CommandFactory 类。当 CommandFactory 返回 Command 对象后,Handler 将调用 Command 上的 Execute 方法来执行请求。具体的实现如下

Handler类:

/// <summary>

/// Handler类

/// </summary>


public class Handler : IHttpHandler

{
    
public void ProcessRequest(HttpContext context)

    
{

        Command command 
= CommandFactory.Make(context.Request.Params);

        command.Execute(context);

    }


    
public bool IsReusable

    
{
        
get

        
{
            
return true;
        }

    }

}

Command接口:

/// <summary>

/// Command

/// </summary>


public interface Command

{
    
void Execute(HttpContext context);
}

CommandFactory类:

/// <summary>

/// CommandFactory

/// </summary>


public class CommandFactory

{
    
public static Command Make(NameValueCollection parms)

    
{

        
string requestParm = parms["requestParm"];

        Command command 
= null;

        
//根据输入参数得到不同的Command对象

        
switch (requestParm)

        
{
            
case "1":

                command 
= new FirstPortal();

                
break;

            
case "2":

                command 
= new SecondPortal();

                
break;

            
default:

                command 
= new FirstPortal();

                
break;
        }


        
return command;

    }

}

RedirectCommand类:

public abstract class RedirectCommand : Command

{
    
//获得Web.Config中定义的key和url键值对,UrlMap类详见下载包中的代码

    
private UrlMap map = UrlMap.SoleInstance;

    
protected abstract void OnExecute(HttpContext context);

    
public void Execute(HttpContext context)

    
{
        OnExecute(context);

        
//根据key和url键值对提交到具体处理的页面

        
string url = String.Format("{0}?{1}", map.Map[context.Request.Url.AbsolutePath], context.Request.Url.Query);

        context.Server.Transfer(url);

    }

}

FirstPortal类:

public class FirstPortal : RedirectCommand

{
    
protected override void OnExecute(HttpContext context)

    
{
        
//在输入参数中加入项portalId以便页面处理

        context.Items[
"portalId"= "1";

    }

}

SecondPortal类:

public class SecondPortal : RedirectCommand

{
    
protected override void OnExecute(HttpContext context)

    
{
        context.Items[
"portalId"= "2";
    }

}

效果及实现要点

1Command模式的根本目的在于将“行为请求者”与“行为实现者”解耦,在面向对象语言中,常见的实现手段是“将行为抽象为对象”。

2.实现Command接口的具体命令对象ConcreteCommand有时候根据需要可能会保存一些额外的状态信息。

3.通过使用Compmosite模式,可以将多个命令封装为一个“复合命令”MacroCommand

4Command模式与C#中的Delegate有些类似。但两者定义行为接口的规范有所区别:Command以面向对象中的“接口-实现”来定义行为接口规范,更严格,更符合抽象原则;Delegate以函数签名来定义行为接口规范,更灵活,但抽象能力比较弱。

5.使用命令模式会导致某些系统有过多的具体命令类。某些系统可能需要几十个,几百个甚至几千个具体命令类,这会使命令模式在这样的系统里变得不实际。

适用性

在下面的情况下应当考虑使用命令模式:

1.使用命令模式作为"CallBack"在面向对象系统中的替代。"CallBack"讲的便是先将一个函数登记上,然后在以后调用此函数。

2.需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命期。换言之,原先的请求发出者可能已经不在了,而命令对象本身仍然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在串形化之后传送到另外一台机器上去。

3.系统需要支持命令的撤消(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo()方法,把命令所产生的效果撤销掉。命令对象还可以提供redo()方法,以供客户端在需要时,再重新实施命令效果。

4.如果一个系统要将系统中所有的数据更新到日志里,以便在系统崩溃时,可以根据日志里读回所有的数据更新命令,重新调用Execute()方法一条一条执行这些命令,从而恢复系统在崩溃前所做的数据更新。

总结

Command模式是非常简单而又优雅的一种设计模式,它的根本目的在于将“行为请求者”与“行为实现者”解耦。

更多的设计模式文章可以访问《.NET设计模式系列文章

 

参考资料

Erich Gamma等,《设计模式:可复用面向对象软件的基础》,机械工业出版社

Robert C.Martin,《敏捷软件开发:原则、模式与实践》,清华大学出版社

阎宏,《Java与模式》,电子工业出版社

Alan Shalloway James R. Trott,《Design Patterns Explained》,中国电力出版社

MSDN WebCast C#面向对象设计模式纵横谈(14)Command命令模式(结构型模式)

袁剑,《领悟Web设计模式》

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2006-07-17 18:11 TerryLee 阅读(26486) 评论(47) 编辑 收藏

 回复 引用 查看   
#1楼 2006-07-18 08:22 stonezhu      
:-)SEEING,希望能加我(zrc000@hotmail.com),和你学更多东西,最近在看你的Enterprise Library 系列...
 回复 引用 查看   
#2楼[楼主] 2006-07-18 08:25 TerryLee      
@stonezhu

已经加了你:)

 回复 引用   
#3楼 2006-07-19 09:10 ADD[未注册用户]
DocumentInvoker invoker = new DocumentInvoker(discmd,undcmd,redcmd);
//全是构造参数,这有点不好吧

 回复 引用   
#4楼 2006-07-19 20:56 星星远好[未注册用户]
看过例子后,还是没有发现为什么要用命令模式。我在别的参考书是如此论述命令模式的。
客户程序在很多时候只关注程序代码段A,但由于要执行A,必须要进行一些操作B,C。为了解决这个问题,引入命令模式,把B,C封装成类D,以及D提供一个接口可以执行代码A。
在常见程序代码,我们执行的GUI就有例子,用户按下按钮,按钮类处理相关事情,然后调用程序员的按钮处理代码。我认为这才是命令模式。
楼主还能再跟我论述一下命令模式吗?我看你的解释,又感到不理解了。

 回复 引用   
#5楼 2006-07-28 12:11 Mok[未注册用户]
I create a simple Command Pattern class, which using reflection to call the command. By using this method, i can have a lot diference command. Here the code

namespace Testing.CommandPattern
{
public interface ICommand
{
Hashtable execute(Hashtable request);
}

//can have many command as u like...
// The command Class name consist of 3 part
// Method + Object+Command , eg View Document Command become ViewDocumentCommand

public class ViewDocumentCommand : ICommand
{
public Hashtable execute(Hashtable request)
{
Console.WriteLine("Implement view documnet task here....");
Hashtable result = new Hashtable();
//proces.....return result...if need to...
return result;
}
}

public class CreateDocumentCommand : ICommand
{
public Hashtable execute(Hashtable request)
{
Console.WriteLine("Implement create documnet task here....");
Hashtable result = new Hashtable();
//proces.....return result...if need to...
return result;
}

}

//Class to dispatch the command,using reflection...
public class CommandDispatch
{
private Hashtable _commands = new Hashtable();

public Hashtable dispatch(Hashtable requestParam)
{
ICommand cmd = null;

string methodid = (string)requestParam["methodid"];
string objectid = (string)requestParam["objectid"];

string classname = "Testing.CommandPattern"+ "." + methodid + objectid + "Command";
try
{
Type cType = Type.GetType(classname, false, true);
cmd = (ICommand)Activator.CreateInstance(cType);
return cmd.execute(requestParam);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}


return null;
}
}
}

//here...how u request a command
namespace CommandPattern
{
class Program
{
static void Main(string[] args)
{
CommandDispatch cmd = new CommandDispatch();
Hashtable param = new Hashtable();
param.Add("methodid", "View");
param.Add("objectid", "Document");

//request View Document command
cmd.dispatch(param);

}
}

}

 回复 引用 查看   
#6楼 2006-08-15 20:47 main      
@ADD
我想这样能够通过DocumentInvoker 控制对document的方法使用:)

 回复 引用   
#7楼 2006-08-28 20:04 joyli[未注册用户]
command模式的核心:实现调用操作的对象和操作的具体实现者之间的解耦。
这样写代码不是更简便吗?为什么要解耦。请教!
class cmd
{
public: virtual void action() = 0;
}
class cmd1 : public cmd
{
public:void action(){this->openfile();}
private: openfile(){};
}
class cmd2 : public cmd
{
public:void action(){this->closefile();}
private closefile(){};
}
int main()
{
cmd* pcmd = new cmd1();//把这里改成factory,可以创建cmd1,cmd2
pcmd->action();
delete pcmd;
return 0;
}


 回复 引用 查看   
#8楼[楼主] 2006-08-29 09:19 TerryLee      
@joyli
1.为什么要解藕参看文章中的内容

2.你写的代码不就是工厂模式了吗?工厂模式与Command模式解决的是不同的问题。请参考
http://terrylee.cnblogs.com/archive/2006/01/04/310716.html

 回复 引用 查看   
#9楼 2006-09-07 15:56 3echo      
写得很好,很容易让人理解。学习

请教一个问题:用简单明了、通俗易懂的语言把问题说清楚,有什么好的办法??需要在哪些方面努力?

 回复 引用 查看   
#10楼[楼主] 2006-09-07 17:20 TerryLee      
@3echo
这个问题好难回答,呵呵
总之一句话,把你所理解的,用一种说话的方式表达出来,我想就通俗易懂了:-)

 回复 引用   
#11楼 2006-10-04 09:03 swafany[未注册用户]
文中提到:这样的使用本来是没有任何问题的,但是我们看到在这个特定的应用中,出现了Undo/Redo的操作,这时如果行为的请求者和行为的实现者之间还是呈现这样一种紧耦合,就不太合适了。
请教为什么不合适?

 回复 引用   
#12楼 2006-10-07 10:59 franckcn[未注册用户]
不知道我这个是不是属于这种模式,类代码
Option Explicit
Dim CalcFlag As Boolean
Dim CalcDic As Dictionary

Private Sub Class_Initialize()
Set CalcDic = New Dictionary
CalcFlag = False
End Sub

Private Sub Class_Terminate()
Set CalcDic = Nothing
End Sub
Public Sub StopCalc()
CalcFlag = False
End Sub
Public Sub StartCalc()
CalcFlag = True
CalcDic.RemoveAll
End Sub
Public Sub Add(ByVal pBarCode As String)
If CalcFlag Then
With CalcDic
If Not .Exists(pBarCode) Then
.Add pBarCode, ""
End If
End With
End If
End Sub
Public Property Get Count() As Long
Count = CalcDic.Count
End Property

客户端代码:
If CmdCalc.Caption = "開始計數" Then
lblCount.Caption = "數量:0"
mCalcCount.StartCalc
CmdCalc.Caption = "停止計數"
Else
CmdCalc.Caption = "開始計數"
If Voice Is Nothing Then
Voice = New SpVoice
End If
Voice.Speak CStr(mCalcCount.Count), SVSFlagsAsync
End If

 回复 引用 查看   
#13楼[楼主] 2006-10-07 11:19 TerryLee      
@franckcn
呵呵,有没有C#的啊,VB.NET的看着好累,主要是我没用过:-)

 回复 引用   
#14楼 2006-10-19 17:04 Frank Huang[未注册用户]
推荐大家去关心下Enterprise Library的Data Access Application Block,里面有很多command pattern例子可做参考。
 回复 引用 查看   
#15楼[楼主] 2006-10-19 19:53 TerryLee      
@Frank Huang
整个EL可以说是学习设计模式的绝佳教材!

 回复 引用   
#16楼 2006-12-12 09:11 balala[未注册用户]
理解不了Command模式,在coding层上感觉和Adapter模式也没什么区别。
 回复 引用   
#17楼 2007-01-09 00:40 青岛开和[未注册用户]
也许兄弟我才疏学浅,看你自己写的列子,让人觉得云里雾里。
原来
static void Main(string[] args)

{
Document doc = new Document();

doc.Display();

doc.Undo();

doc.Redo();
}
经过你一番命令模式之后变成

static void Main(string[] args)

{

Document doc = new Document();


DocumentCommand discmd = new DisplayCommand(doc);

DocumentCommand undcmd = new UndoCommand(doc);

DocumentCommand redcmd = new RedoCommand(doc);


DocumentInvoker invoker = new DocumentInvoker(discmd,undcmd,redcmd);

invoker.Display();

invoker.Undo();

invoker.Redo();

}
后面的invoker.Display();invoker.Undo();invoker.Redo();
其实还是调用的
doc.Display();

doc.Undo();

doc.Redo();
这不是南辕北辙吗?
好比你洗澡,本来就是光着身子,现在是先穿上棉袄,再脱下来。

也许我理解有误,请牛人指点。

相反第二个例子也就是.NET中的Command模式更让人信服。

 回复 引用   
#18楼 2007-01-15 00:13 sh_city[未注册用户]
晕,我看了李建忠老师也没弄懂这个Command 模式,现在看了您的这个,也还是没弄懂Command ,可能是我太笨了,无法领会这种精深的技术.
 回复 引用 查看   
#19楼[楼主] 2007-01-15 08:31 TerryLee      
@sh_city
刚开始学确实会有一头雾水的感觉,慢慢来,不要着急:)

 回复 引用   
#20楼 2007-03-05 17:23 Michael[未注册用户]
command模式其实很好理解的,比如窗体设计中:窗体中的button的click事件,button是命令者,而事件执行的代码就是命令.命令模式就是把事件的执行又封装一层,使其与命令者(button)不在同一类中.实现命令者(button)与执行命令(button_click事件)的隔离
 回复 引用 查看   
#22楼 2007-07-10 00:49 黄志强      
好文章,最近也在用Front Controller
 回复 引用   
#23楼 2007-08-31 12:00 华为菜鸟[未注册用户]
好东西,值得学习
 回复 引用 查看   
#24楼 2007-09-17 17:50 王晓成      
纯粹为了方便,引用了terrylee的文章
 回复 引用   
#25楼 2007-09-27 10:44 longer[未注册用户]
public abstract class DocumentCommand

{
Document _document;///这里应该是Protected

public DocumentCommand(Document doc)

{
this._document = doc;
}

/**//// <summary>

/// 执行

/// </summary>

public abstract void Execute();

}

 回复 引用 查看   
#26楼 2007-12-13 09:59 书生多命贱      
楼主的 组合模式的单词拼错了,在文章 效果及实现要点 的第3点,“通过使用Compmosite模式”,应该是“Composite”
 回复 引用 查看   
#27楼[楼主] 2007-12-13 12:42 TerryLee      
@书生多命贱
谢谢

 回复 引用 查看   
#28楼 2008-12-26 13:56 Fengdesudu      
不错,学习中。
 回复 引用 查看   
#29楼[楼主] 2009-01-04 11:32 TerryLee      
@Fengdesudu
谢谢:)

 回复 引用   
#30楼 2009-06-01 00:25 nick_temp[未注册用户]
--引用--------------------------------------------------
swafany: 文中提到:这样的使用本来是没有任何问题的,但是我们看到在这个特定的应用中,出现了Undo/Redo的操作,这时如果行为的请求者和行为的实现者之间还是呈现这样一种紧耦合,就不太合适了。
<br>请教为什么不合适?
--------------------------------------------------------
我也很想知道,为什么不适合,直接说不合适未免太过于牵强

 回复 引用 查看   
#31楼 2009-06-01 08:58 书生多命贱      
因为紧耦合了,所以就不适合!这个已经说的很清楚了!
 回复 引用 查看   
#32楼 2009-07-11 22:57 zorwi      
感觉到了headFirst的影子。。。
 回复 引用 查看   
#33楼 2009-07-14 10:16 wycowen      
如果各个命令的参数不同(类型与个数都不同),基类应该如何设计?
 回复 引用 查看   
#34楼 2009-08-20 11:35 月云      
李大哥,我还是有些不明白~~ 所谓的命令模式,精华思想仅仅是通过将命令的请求者和命令的执行者分离到不同的层这么简单吗?
 回复 引用 查看   
#35楼 2009-08-20 22:11 会长      
哦,看了这个模式让我想起了以前做Java时用过的Struts框架,这个应该是命令模式的一个很好的应用吧,定义不同的action,其实就是不同的指令,将哪个操作用哪个Action的信息放在配置文件中。。好像也哟个Execute的方法。。
李哥,再向您请教一个题外问题,我最近很想学习一下项目架构方面的知识,不知道看什么书,有什么资料,您可否指教一二。我对这方面没有太多的研究,来公司工作的两年也就对公司用的框架很熟悉,没有见识过大型项目的框架结构,如果研究一下petShop是不是个好主意呢?

 回复 引用 查看   
#36楼 2009-08-20 22:21 会长      
@书生多命贱
是否可以这样理解,面向对象语言的一大好处就是可以封装变化,如封装某些变化的实体,万般皆对象,某些动作也可以被看作对象,也可以被封住起来,如果这一动作是变化的、不确定的,就应该被封装起来,这样一来,如果有新的动作加入,只需增加代码,而不像修改代码,正好符合面向对象设计的“对修改关闭,对扩展开放“的原则。有利于维护。

 回复 引用   
#37楼 2009-09-02 14:28 小小小二[未注册用户]
public DisplayCommand(Document doc)

: base(doc)
{

}

public override void Execute()

{
_document.Display();
}

这里的 _document.Display()
是哪里声明的...

 回复 引用 查看   
#38楼 2009-10-07 22:58 圆月|弯刀      
如果在Invoker里面进行对客户请求的判断,然后直接调用的Docment的方法跟实例化命令对象再执行有什么却别吗?这样一样不一样解了请求者和实现者的耦合,因为一样有着一个中间层“Invoker”,我以前没学设计模式的时候就这么做过。
当然命令模式对于命令的拓展很是方便,因为可以方便的增加命令类,且类中方法不一定都是封装的Documen的,不知道这句话我说的对不对?

 回复 引用   
#39楼 2009-11-08 11:53 Jiessie is me![未注册用户]
看了楼主的command模式后,我也还是有些不解。当然,楼主的说明,尤其是document类图能够很好的解决这些疑惑,只是感觉楼主在document的command模式中设计的类层次结构有点问题吧。既然command解耦是通过加入一个中间层断开命令调用者和执行者的直接接触,因此中间层即invoker应该根据命令请求负责匹配合适的command,然后执行命令吧。而楼主把所有command的构造放在了命令调用者那里了,这样做并没有很好的解耦吧。倒是在asp.net中提到的那个例子很好的说明了command模式结构。

我也是个模式的初探者,以上是偶的一些愚见,说的不对的地方,还请楼主见谅。

 回复 引用 查看   
#40楼 2009-12-30 13:15 sun shine      
请问: uml 中的实线箭头和虚线箭头 有什么区别?
 回复 引用 查看   
#41楼 2010-09-24 16:16 阿张      
回LS的
好像一个是依赖 一个是实现

 回复 引用 查看   
#42楼 2011-03-20 10:00 shfong      
引用Jiessie is me!:
看了楼主的command模式后,我也还是有些不解。当然,楼主的说明,尤其是document类图能够很好的解决这些疑惑,只是感觉楼主在document的command模式中设计的类层次结构有点问题吧。既然command解耦是通过加入一个中间层断开命令调用者和执行者的直接接触,因此中间层即invoker应该根据命令请求负责匹配合适的command,然后执行命令吧。而楼主把所有command的构造放在了命令调用者那里了,这样做并没有很好的解耦吧。倒是在asp.net中提到的那个例子很好的说明了command模式结构。

我也是个模式的初探者,以上是偶的一些愚见,说的不...


我在这里也有些不解,我觉得写成这样应该更好些吧
public class DocumentInvoker

{
private DocumentCommand documentCommand;

public void SetDocumentCommand (DocumentCommand command)
{
this.documentCommand= command;
}

public void ExecuteCommand()
{
documentCommand.Execute();
}
}

这是参考http://www.dofactory.com/Patterns/PatternCommand.aspx#_self1