(14)行为型模式——命令

行为型模式——命令(Command)

问题背景

当遇到以下需求时,考虑使用命令:

  1. 将用户和实际执行请求的对象解耦。
  2. 支持撤销操作。
  3. 多种操作对应同一种请求。
  4. 一个请求执行一串连续操作。

命令的应用场景非常广泛,一个不得不提的就是菜单功能。考虑一个菜单,首先,用户只需要在菜单上做同样的操作(选择一个选项)就可以实现各种截然不同的功能,这说明菜单将用户和实际操作解耦了;其次,绝大多数软件的菜单都会有“撤销”选项;不同的选项也可能对应同一种操作,一个选项可能是几个选项的复合指令。菜单的需求几乎完美契合了命令模式的前提条件,于是使用命令实现菜单成为了首选方案。

解决方案

一旦需要将用户与具体实现解耦,首先应该想到的就是接口。我们想要让用户和实际执行操作的对象解耦,只需要将实际执行操作的对象进行抽象。因为这类对象的主要功能就是执行一个操作,所以我们将其抽象为“命令”。我们提取一个命令接口ICommand,包含执行操作(如果需要,也可以包含撤销操作),所有具体命令都实现这个接口。如此一来,用户在使用系统时只需要调用执行或撤销方法就能完成不同的操作。

用户只需要知道,命令对象一定会完成规定的操作,至于如何完成,Who cares?这也就意味着,命令对象的实现方式是没有约束的,我们大可以在命令对象的接口方法里直接实现功能。如果不行,还可以让命令对象仅作为一个中转,将命令传递给其他可以处理的对象去处理。如果情况更复杂,我们甚至可以玩套娃,让一个命令对象去调用其他的一串命令对象,实现功能组合(宏)。

从语义上讲,命令对象的职责只应该是执行一项操作。但是,一个菜单项可能有很多其他信息,比如所处的位置、提示文字等,这些信息不应该由命令对象储存。可以用一个菜单项对象记录这些信息,并让菜单项对象聚合一个命令对象。

根据以上的描述设计一个程序,结构如下:
程序结构
如图,Close是一个简单命令,直接由命令对象实现,Save命令需要另一个对象来辅助完成,SaveClose是一个复合命令,它先执行Save命令,然后执行Close命令。MenuItem是命令的载体,同时记录了一些其他信息,用户只需要跟MenuItem打交道,甚至不知道系统内部命令类的存在。

效果

  1. 将抽象和实现分离,提高了程序的可扩展性。
  2. 隐藏了系统内部的复杂性,具有统一性。
  3. 能很好地支持撤销操作。
  4. 能组合形成复杂的命令。

相关模式

  1. 复合:命令可以通过复合形成更复杂的宏命令。
  2. 备忘录:命令配合备忘录可以很好地实现撤销功能。

实现

using System;

namespace Command
{
    class Client
    {
        public interface ICommand
        {
            void Execute();
        }

        public class Close : ICommand
        {
            public void Execute()
            {
                Console.WriteLine("关闭");
            }
        }

        public class Save : ICommand
        {
            private SaveHelper helper = new SaveHelper();
            public void Execute()
            {
                helper.Save();
            }
        }

        public class SaveClose : ICommand
        {
            private ICommand[] commands = new ICommand[] { new Save(), new Close() };
            public void Execute()
            {
                foreach (var item in commands)
                {
                    item.Execute();
                }
            }
        }

        public class SaveHelper
        {
            public void Save()
            {
                Console.WriteLine("保存");
            }
        }

        public class MenuItem
        {
            private ICommand command;
            public string desc;
            public MenuItem(ICommand command, string desc)
            {
                this.command = command;
                this.desc = desc;
            }
            public void Execute()
            {
                Console.WriteLine($"选择了选项: {desc}");
                command.Execute();
            }
        }

        static void Main(string[] args)
        {
            Console.WriteLine("在系统内部生成菜单...");
            var close = new MenuItem(new Close(), "关闭");
            var save = new MenuItem(new Save(), "保存");
            var save_close = new MenuItem(new SaveClose(), "保存并关闭");

            Console.WriteLine("用户使用菜单...");
            close.Execute();
            save.Execute();
            save_close.Execute();
        }
    }
}

运行结果

发布了27 篇原创文章 · 获赞 41 · 访问量 2076

猜你喜欢

转载自blog.csdn.net/DIAX_/article/details/104219016
今日推荐