命令行为模式(Command Behavior pattern)允许你在表现层(presentation layer)封装行为,使得采用自己喜欢的方法组织、跟踪、撤销和调用这些行为更加容易。
在现实生活中,次序是难以控制的。一旦某种东西处于运动状态,我们就很难操作这种动作的离散部分。当然,在现实生活中是不可能撤销某种动作的。但是在编程过程中,次序却不是难以琢磨的。如果你的计划是正确的,你就可以定义行动,接着用你喜欢的方式来控制这些行动。实现这种操作的一个非常有用的工具是命令行为(Command Behavior)模式。
命令行为模式是我们可以使用的一种简单模式。它在行为(action)概念的具体化和撤销行为方面显得尤其有益。把行为转换到对象中也是一条非常有序的途径,它可以确保每个操作都会集中在实现该操作的一部分代码上。
在本文中我们将研究命令行为模式的使用方法,你会感觉它比较有趣。我给窗体添加了一个球的图片,并且实现了表现球的移动过程的命令。每个球命令都被放入栈中,允许你撤销球的移动,或者重新查看球的移动。在稍微修补一下代码之后,你可以发现把表现层(GUI)的操作转换为命令对象使得我们使用多种方式(例如按钮和菜单)封装、组织、跟踪、撤销和调用操作容易多了。
定义命令(Command)类
实现命令行为的一个普通的途径是定义一个带有Do和Undo方法的基类(或接口)。Undo执行与Do操作相反的行为。Do操作是什么样子都无关紧要,它可以是任何东西("Do"在VB6中是保留字,因此我把"Execute"作为方法的名字)。
我的例子实现了四个移动命令。每个命令从上下左右四个方向中选择一个方向执行移动操作。每个命令的Undo操作采用相反的方向调用移动操作。很明显,我并没有限定两维的、直线方向。我可以模拟三维的或者三角法(trigonometric)运算规则的基本移动。现在我聚焦于该命令类。
使用公用基类的原因在于代码可以多形态地(polymorphically)调用Do或Undo操作,而不用关心命令对象的特定实例。列表1显示了基本的命令和所有四个衍生命令类的实现。由于VB6不支持类继承,我就使用了接口继承。
列表1
' ICommand.cls
Public Sub Execute()
End Sub
Public Sub Undo()
End Sub
Public Property Set Form(ByVal Form As Form1)
End Property
' DownCommand.cls
Option Explicit
Implements ICommand
Private FForm As Form1
Private Sub ICommand_Execute()
FForm.MoveDown
End Sub
Private Sub ICommand_Undo()
FForm.MoveUp
End Sub
' LeftCommand.cls
Public Property Set ICommand_Form(ByVal Form As Form1)
Set FForm = Form
End Property
Option Explicit
Implements ICommand
Private FForm As Form1
Private Sub ICommand_Execute()
FForm.MoveLeft
End Sub
Private Sub ICommand_Undo()
FForm.MoveRight
End Sub
Public Property Set ICommand_Form(ByVal Form As Form1)
Set FForm = Form
End Property
' RightCommand.cls
Option Explicit
Implements ICommand
Private FForm As Form1
Private Sub ICommand_Execute()
FForm.MoveRight
End Sub
Private Sub ICommand_Undo()
FForm.MoveLeft
End Sub
Public Property Set ICommand_Form(ByVal Form As Form1)
Set FForm = Form
End Property
' UpCommand.cls
Option Explicit
Implements ICommand
Private FForm As Form1
Private Sub ICommand_Execute()
FForm.MoveUp
End Sub
Private Property Set ICommand_Form(ByVal RHS As Form1)
Set FForm = RHS
End Property
Private Sub ICommand_Undo()
FForm.MoveDown
End Sub
[责任编辑:cndownzcom]
请注意在列表1中ICommand使用了前缀I。这是接口的一个通俗的前缀符号,在多种语言中被广泛的应用。它的目的是帮助读者记住该模块只包含定义。同时还要注意所有的方向命令中都使用了Implements语句。这确保了每个类最少拥有ICommand接口。其结果是我可以定义一个ICommand变量,并给它指定实现了ICommand类的任何实例。
最后我还要指出,每个命令都保持了特定的Form(Form1)的指针。其原因在于Form1包含了自己的边界和我希望用命令操作的控件的信息。
建立GUI
该程序的GUI由Form1表现,它包含了一个图像(image)控件,在该控件中有一个球的位图。该窗体还包含了一个对象集合,我把这个集合作为先前命令的堆栈。通过把每条命令添加到该集合的末尾,以及从集合末尾弹出命令,我可以相反地跟踪命令的执行过程。从本质上来说,该集合扮演了堆栈的角色。我在命令执行之后压入(push)命令,弹出(pop)命令调用Undo操作。
为了演示Undo操作,我定义了Edit|Undo菜单和Edit|Rewind操作。Undo从堆栈中弹出一条命令并调用Undo。Rewind弹出所有命令,在每个命令上调用Undo。列表2包含了Form1的代码。
列表2
Option Explicit
Private stack As Collection
Private Const MOVEMENT_AMOUNT As Integer = 50
Private Sub AboutMenu_Click()
Const About As String = "Command Pattern Demo" + vbCrLf + _
"Copyright (c) 2004. All Rights Reserved" + vbCrLf + _
"Written By Paul Kimmel. pkimmel@softconcepts.com"
MsgBox About, vbInformation + vbOKOnly, "About"
End Sub
Private Sub ExitMenu_Click()
End
End Sub
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
Select Case KeyCode
Case vbKeyBack
UndoCommand
Case vbKeyLeft
Call ProcessCommand(New LeftCommand)
Case vbKeyUp
Call ProcessCommand(New UpCommand)
Case vbKeyRight
Call ProcessCommand(New RightCommand)
Case vbKeyDown
Call ProcessCommand(New DownCommand)
End Select
End Sub
Private Function PopStack() As ICommand
On Error GoTo Catch
Debug.Print "Stack count: " & stack.Count
If (stack.Count = 0) Then
Beep
Set PopStack = Nothing
Exit Function
End If
Set PopStack = stack.Item(stack.Count)
Call stack.Remove(stack.Count)
Exit Function
Catch:
Debug.Print Err.Description
Set PopStack = Nothing
End Function
Private Sub PushStack(ByVal command As ICommand)
On Error GoTo Catch
Call stack.Add(command)
Debug.Print "Stack count: " & stack.Count
Exit Sub
Catch:
Debug.Print Err.Description
End Sub
Public Sub UndoCommand()
Dim command As ICommand
Set command = PopStack()
If (command Is Nothing) Then Exit Sub
command.Undo
End Sub
Private Sub ProcessCommand(ByVal command As ICommand)
Set command.Form = Me
command.Execute
Call PushStack(command)
End Sub
Public Sub MoveDown()
Debug.Print "Moving Down"
If (Image1.Top > Height) Then
Image1.Top = 0 - Image1.Height + MOVEMENT_AMOUNT
Else
Image1.Top = Image1.Top + MOVEMENT_AMOUNT
End If
End Sub
Public Sub MoveUp()
Debug.Print "Moving Up"
If (Image1.Top + Image1.Height < 0) Then
Image1.Top = Height - MOVEMENT_AMOUNT
Else
Image1.Top = Image1.Top - MOVEMENT_AMOUNT
End If
End Sub
Public Sub MoveLeft()
Debug.Print "Moving Left"
If (Image1.Left + Image1.Width < 0) Then
Image1.Left = Width - MOVEMENT_AMOUNT
Else
Image1.Left = Image1.Left - MOVEMENT_AMOUNT
End If
End Sub
Public Sub MoveRight()
Debug.Print "Moving Right"
If (Image1.Left > Width) Then
Image1.Left = 0 - Image1.Width + MOVEMENT_AMOUNT
Else
Image1.Left = Image1.Left + MOVEMENT_AMOUNT
End If
End Sub
Private Sub Form_Load()
Set stack = New Collection
End Sub
Private Sub RewindMenu_Click()
While stack.Count > 0
UndoCommand
Wend
End Sub
Private Sub UndoMenu_Click()
Call UndoCommand
End Sub
[责任编辑:cndownzcom]
下一步,我通过把窗体的KeyPreview属性设置为True并建立与该按键相关的命令对象,把有关的方向箭头按键与每条命令关联起来。例如vbKeyUp应该建立一个UpCommand对象。接着我处理这些命令(我也可以使用工厂创建型模式把命令对象的创建过程移动到命令类的内部)。
ProcessCommand被定义为把Form1的指针赋予某个命令的Form属性、执行该命令并把该命
【中国下载站】【设为主页】【收藏本页】【打印本文】【回到顶部】【关闭此页】