设计模式-可复用版-Command 命令模式

235 篇文章 22 订阅

在Command里,包含了我要操作、处理的对象。比如粘贴命令中,肯定包含文档Document本身 。

Command模式代码结构很简单,定义一个Command接口,包含了一个Execute()的方法:

namespace PureMVC.Interfaces

{

    public interface ICommand : INotifier

    {

        void Execute(INotification Notification);

    }

}

这是PureMVC Framework中ICommand接口的实现,只有一个Execute,包含了一个INotification,这是一个消息体:

namespace PureMVC.Interfaces

{

    public interface INotification

    {

        string Name { get; }

        object Body { get; set; }

        string Type { get; set; }

        string ToString();

    }

}   

我们在使用中,一定会涉及到需要参数化的地方,INotification便是负责参数的部分。

INotifier这是通知的接口,在Command中,我们也会去执行其它的命令,在PureMVC Framework中,命令的执行是通过INotifier实现,在上面的例子中,不必去关心,不同的框架实现基本上的思路是一样的,只需要知道,Command中,至少会有一个Execute方法。

Command也支持撤消的操作,对于这种需求,我们通常也会有一个UnExecute,用于撤消原来的操作,但这时候,我们要撤销,就需要记录在撤销前的操作,这里就会涉及到Memento备忘录模式,用于保存对象的内部状态,如果对象比较复杂,又会涉及到深浅拷贝的问题,这里又会涉及到Prototype原型设计模式。

存储的操作也可以定义到Command中,使执行和撤消的操作进行分离。  撤消这里又叫rollback or discard回滚,后退,比如我们在浏览网页的时候,返回上一页。   有时候,我们要依次执行多条不同的命令,可以定义一个MacroCommand,比如在初次进入游戏时,检测资源更新,初始化各种管理器,加载游戏的静态数据,读取存档,加载首场景等必须资源,这些不同的请求,都在不同的Command中,可以通过MacroCommand来 依次的执行。  

MacroCommand实现,只需要提供一个List:    

public class GameStartCommand : MacroCommand

{

    protected override void InitializeMacroCommand()

    {

        AddSubCommand(() => new InitAudioManagerCommand());

        AddSubCommand(() => new InitUIManagerCommand());

        AddSubCommand(() => new LoadPlayerPrefsCommand());

        AddSubCommand(() => new StartLoginCommand());

    }

}

    

Command结构:    

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1

 

Client->当前运行的程序

Invoker->执行命令,调用者,Invoker并不知道具体的Command(ConcreteCommand),只知道ICommand interface.

Receiver->定义在Command中,由Receiver执行具体的Action

上图可以看到Command做了一层简单的封装,具体的逻辑还是由Reciever->Action()执行。

我们在使用PureMVC Framework中,具体的请求逻辑基本上都是在Command中写的,除非一些通用的操作,我们会定义在 Reciever中。

其它具体的请求,均在Command定义,这样会更加的合理,请求之间解耦。

涉及到请求的变更,只要不涉及到公共的部分,只会修改某一个具体的Command,从而不会影响到其它的请求。

增加新的Command很容易,它无需去改变已有的类。

对于没有取消操作和无参数的Command,我们可以简单的使用范型或是委托实同现,减少子类的生成,提高代码的重用性,前提是,具体的Action 是在Reciever中实现的,简单的实现代码如下:

//测试代码: 

SimpleTestCommand command = new SimpleTestCommand(new Copy().CopyFile);

command.Execute();

SimpleTestCommand command1 = new SimpleTestCommand(new Paste().PasteFile);

command1.Execute();


 

 CoyFile,PasteFile不需要定义在两个Command子类中。


 

public interface ICommand

{

    void Execute();

}

public class SimpleTestCommand : ICommand

{

    public Action action;

    public SimpleTestCommand(Action action)

    {

        this.action = action;

    }

    public void Execute()

    {

        action?.Invoke();

    }

}

public class Copy

{

    public void CopyFile()

    {

        Debug.Log("Copy File");

    }

}

public class Paste

{

    public void PasteFile()

    {

        Debug.Log("Paste File");

    }

}

下在是wikipedia上的应用例子:




using System;

namespace CommandPattern

{

    public interface ICommand

    {

        void Execute();

    }

    /* The Invoker class */

    public class Switch

    {

        ICommand _closedCommand;

        ICommand _openedCommand;

        public Switch(ICommand closedCommand, ICommand openedCommand)

        {

            this._closedCommand = closedCommand;

            this._openedCommand = openedCommand;

        }

        // Close the circuit / power on

        public void Close()

        {

            this._closedCommand.Execute();

        }

        // Open the circuit / power off

        public void Open()

        {

            this._openedCommand.Execute();

        }

    }

    /* An interface that defines actions that the receiver can perform */

    public interface ISwitchable

    {

        void PowerOn();

        void PowerOff();

    }

    /* The Receiver class */

    public class Light : ISwitchable

    {

        public void PowerOn()

        {

            Console.WriteLine("The light is on");

        }

        public void PowerOff()

        {

            Console.WriteLine("The light is off");

        }

    }

    /* The Command for turning off the device - ConcreteCommand #1 */

    public class CloseSwitchCommand : ICommand

    {

        private ISwitchable _switchable;

        public CloseSwitchCommand(ISwitchable switchable)

        {

            _switchable = switchable;

        }

        public void Execute()

        {

            _switchable.PowerOff();

        }

    }

    /* The Command for turning on the device - ConcreteCommand #2 */

    public class OpenSwitchCommand : ICommand

    {

        private ISwitchable _switchable;

        public OpenSwitchCommand(ISwitchable switchable)

        {

            _switchable = switchable;

        }

        public void Execute()

        {

            _switchable.PowerOn();

        }

    }

    /* The test class or client */

    internal class Program

    {

        public static void Main(string[] arguments)

        {

            string argument = arguments.Length > 0 ? arguments[0].ToUpper() : null;

            ISwitchable lamp = new Light();

            // Pass reference to the lamp instance to each command

            ICommand switchClose = new CloseSwitchCommand(lamp);

            ICommand switchOpen = new OpenSwitchCommand(lamp);

            // Pass reference to instances of the Command objects to the switch

            Switch @switch = new Switch(switchClose, switchOpen);

            if (argument == "ON")

            {

                // Switch (the Invoker) will invoke Execute() on the command object.

                @switch.Open();

            }

            else if (argument == "OFF")

            {

                // Switch (the Invoker) will invoke the Execute() on the command object.

                @switch.Close();

            }

            else

            {

                Console.WriteLine("Argument \"ON\" or \"OFF\" is required.");

            }

        }

    }

}


 

--

Switch包含了两个Command的引用,但并没有指定具体的Command:

ICommand _closedCommand; 

ICommand _openedCommand;  

wikipedia上有许多关于Command的应用场景,比如UI中的按钮,菜单项, Browser的向导Wizard,你可以在很多的场景中进行应用,他并不固定于某一个方面,核心是“将一个请求封闭成一个对象”,这样做的好处是可以提高代码的复用性,请求的调用者与具体的请求之间解耦,提高灵活性,方便去扩展,并且请求与请求之间也进行了分离, 容易去添加更多的Command.  

:记录得有些乱,其实应该用几句话或是更小的语言进行精练的总结他的概念,意图,应用场景,注意事项,优点,缺点

 

https://gameinstitute.qq.com/community/detail/133055

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值