模式简介
Command模式就是针对调用者不同的操作而执行不同的命令。
模式UML图
代码示例(C#)
提示:这里涉及与其它模式中的代码的联动,包括Iterator模式与Memento模式,所以这里单独的代码示例无法直接运行,如果需要运行示例代码可以手动复制上述两个模式的示例代码进行联动,或者从本栏目的资源篇“设计模式代码示例合集”中下载所有完整代码资源。
using System;
using System.Collections.Generic;
using System.Text.Json;
namespace Test1
{
//测试类
class Test41
{
public void Test()
{
Text text = Text.GetInstance();
text.Add("Hello World.").Add("JoJo.").Copy()
.Delete("JoJo.").Delete("Hello World.").Save()
.Add("Hello,I am Kaka.").Undo().Undo().Undo()
.Undo().Undo().Show().Load().Undo().Undo().Show();
}
}
//文本操作命令类型
public enum CommandType
{
AddCommand, DeleteCommand, CopyCommand
}
//文本类,文本类的一个实例就类似于一个.txt文件
public class Text
{
private string text = "";
private Text() { }
private class TextHandler
{
public static Text instance = new Text();
public static CommandChainManager commandChainManager = new CommandChainManager();
public static CommandChain commandChain = new CommandChain();
public static IDManager idManager = new IDManager();
}
public static Text GetInstance()
{
return TextHandler.instance;
}
public Text Add(string p_text)
{
int v_id = TextHandler.idManager.GetIndex();
TextHandler.commandChain.Add(new AddCommand(p_text, v_id));
TextHandler.commandChain.Do(ref text);
return GetInstance();
}
public Text Delete(string p_text)
{
int v_id = TextHandler.idManager.GetIndex();
TextHandler.commandChain.Add(new DeleteCommand(p_text, v_id));
TextHandler.commandChain.Do(ref text);
return GetInstance();
}
public Text Copy()
{
int v_id = TextHandler.idManager.GetIndex();
TextHandler.commandChain.Add(new CopyCommand(v_id));
TextHandler.commandChain.Do(ref text);
return GetInstance();
}
public Text Copy(string p_copyContent)
{
int v_id = TextHandler.idManager.GetIndex();
TextHandler.commandChain.Add(new CopyCommand(p_copyContent, v_id));
TextHandler.commandChain.Do(ref text);
return GetInstance();
}
public Text Undo()
{
TextHandler.commandChain.Undo(ref text, TextHandler.idManager);
return GetInstance();
}
public Text Save()
{
foreach (ITextCommand c in TextHandler.commandChain.commands)
{
c.SaveCommand(TextHandler.commandChain);
}
TextHandler.commandChainManager.SaveText(text);
TextHandler.commandChainManager.SaveCommands(TextHandler.commandChain);
return GetInstance();
}
public Text Load()
{
TextHandler.commandChainManager.LoadText(ref text);
TextHandler.commandChainManager.LoadCommand(ref TextHandler.commandChain);
TextHandler.commandChain.commands.Clear();
TextHandler.idManager.ResetIndex();
foreach (CSFParameter[] param in TextHandler.commandChain.parameters)
{
foreach (CSFParameter p in param)
{
if (p.parameterName.Equals("commandType"))
{
CommandType v_type;
if (Enum.TryParse<CommandType>(((JsonElement)p.value).GetString(), false, out v_type))
{
switch (v_type)
{
case CommandType.AddCommand:
TextHandler.commandChain.commands.Add(new AddCommand("", TextHandler.idManager.GetIndex()));
break;
case CommandType.DeleteCommand:
TextHandler.commandChain.commands.Add(new DeleteCommand("", TextHandler.idManager.GetIndex()));
break;
case CommandType.CopyCommand:
TextHandler.commandChain.commands.Add(new CopyCommand(TextHandler.idManager.GetIndex()));
break;
default: break;
}
}
}
}
}
TextHandler.commandChain.Reset();
foreach (ITextCommand c in TextHandler.commandChain.commands)
{
c.LoadCommand(TextHandler.commandChain);
}
return GetInstance();
}
public Text Show()
{
Console.WriteLine("The text:" + text);
return GetInstance();
}
}
//文本添加命令
public class AddCommand : ITextCommand
{
private string addContent;
private static List<int> indexs = new List<int>();
private static int count = 0;
private static CSFParameter[] parameters;
private static CommandType commandType;
private int id;
public void Print()
{
string v_str = "[AddCommand]:" + addContent + " [";
for (int i = 0; i < indexs.Count; i++)
{
if (i < indexs.Count - 1) v_str += indexs[i] + ",";
else v_str += indexs[i];
}
v_str += "] " + count + " " + id;
Console.WriteLine(v_str);
}
public AddCommand(string p_addContent, int p_id)
{
addContent = p_addContent;
id = p_id;
commandType = CommandType.AddCommand;
}
public bool Do(ref string p_text)
{
p_text = Add(p_text);
return true;
}
public bool Undo(ref string p_text)
{
p_text = UndoAdd(p_text);
return true;
}
public int GetId()
{
return id;
}
public void SaveCommand(CommandChain p_commandChain)
{
CSFParameter p1 = new CSFParameter("addContent", DataType.String, addContent);
CSFParameter p2 = new CSFParameter("indexs", DataType.IntArray, indexs.ToArray());
CSFParameter p3 = new CSFParameter("count", DataType.Int, count);
CSFParameter p4 = new CSFParameter("id", DataType.Int, id);
CSFParameter p5 = new CSFParameter("commandType", DataType.String, commandType.ToString());
parameters = new CSFParameter[] { p1, p2, p3, p4, p5 };
p_commandChain.SaveCommand(parameters);
}
public void LoadCommand(CommandChain p_commandChain)
{
CSFParameter[] v_parameters;
p_commandChain.LoadCommand(out v_parameters, this);
if (v_parameters != null)
{
foreach (CSFParameter p in v_parameters)
{
switch (p.parameterName)
{
case "addContent":
addContent = ((JsonElement)p.value).GetString();
break;
case "indexs":
indexs.Clear();
JsonElement v_array = (JsonElement)p.value;
for (int i = 0; i < v_array.GetArrayLength(); i++)
{
indexs.Add(v_array[i].GetInt32());
}
break;
case "count":
count = ((JsonElement)p.value).GetInt32();
break;
case "id":
id = ((JsonElement)p.value).GetInt32();
break;
case "commandType":
CommandType v_commandType;
if (Enum.TryParse<CommandType>(((JsonElement)p.value).GetString(), false, out v_commandType))
commandType = v_commandType;
break;
default: break;
}
}
}
}
public CommandType GetCommandType()
{
return commandType;
}
private string Add(string p_text)
{
if (indexs.Count == 0) indexs.Add(0);
else indexs.Add(p_text.Length);
count++;
return p_text + addContent;
}
private string UndoAdd(string p_text)
{
string v_text = p_text;
if (count == 0 || indexs[count - 1] > v_text.Length) return v_text;
else if (indexs[count - 1] == 0) v_text = "";
else v_text = v_text.Substring(0, indexs[count - 1]);
indexs.RemoveAt(count - 1);
count--;
return v_text;
}
}
//文本删除命令
public class DeleteCommand : ITextCommand
{
private string deleteContent;
private static List<int> indexs = new List<int>();
private static int count = 0;
private static CSFParameter[] parameters;
private static CommandType commandType;
private int id;
public void Print()
{
string v_str = "[DeleteCommand]:" + deleteContent + " [";
for (int i = 0; i < indexs.Count; i++)
{
if (i < indexs.Count - 1) v_str += indexs[i] + ",";
else v_str += indexs[i];
}
v_str += "] " + count + " " + id;
Console.WriteLine(v_str);
}
public DeleteCommand(string p_deleteContent, int p_id)
{
deleteContent = p_deleteContent;
id = p_id;
commandType = CommandType.DeleteCommand;
}
public bool Do(ref string p_text)
{
p_text = Delete(p_text);
return true;
}
public bool Undo(ref string p_text)
{
p_text = UndoDelete(p_text);
return true;
}
public void SaveCommand(CommandChain p_commandChain)
{
CSFParameter p1 = new CSFParameter("deleteContent", DataType.String, deleteContent);
CSFParameter p2 = new CSFParameter("indexs", DataType.IntArray, indexs.ToArray());
CSFParameter p3 = new CSFParameter("count", DataType.Int, count);
CSFParameter p4 = new CSFParameter("id", DataType.Int, id);
CSFParameter p5 = new CSFParameter("commandType", DataType.String, commandType.ToString());
parameters = new CSFParameter[] { p1, p2, p3, p4, p5 };
p_commandChain.SaveCommand(parameters);
}
public void LoadCommand(CommandChain p_commandChain)
{
CSFParameter[] v_parameters;
p_commandChain.LoadCommand(out v_parameters, this);
if (v_parameters != null)
{
foreach (CSFParameter p in v_parameters)
{
switch (p.parameterName)
{
case "deleteContent":
deleteContent = ((JsonElement)p.value).GetString();
break;
case "indexs":
indexs.Clear();
JsonElement v_array = (JsonElement)p.value;
for (int i = 0; i < v_array.GetArrayLength(); i++)
{
indexs.Add(v_array[i].GetInt32());
}
break;
case "count":
count = ((JsonElement)p.value).GetInt32();
break;
case "id":
id = ((JsonElement)p.value).GetInt32();
break;
case "commandType":
CommandType v_commandType;
if (Enum.TryParse<CommandType>(((JsonElement)p.value).GetString(), false, out v_commandType))
commandType = v_commandType;
break;
default: break;
}
}
}
}
public int GetId()
{
return id;
}
public CommandType GetCommandType()
{
return commandType;
}
private string Delete(string p_text)
{
if (deleteContent.Length > p_text.Length) return p_text;
else
{
for (int i = p_text.Length - 1; i >= 0; i--)
{
if (p_text[i] == deleteContent[0] && i + deleteContent.Length - 1 < p_text.Length)
{
string v_text = p_text.Substring(i, deleteContent.Length);
if (v_text.Equals(deleteContent))
{
indexs.Add(i);
count++;
string sub_text = p_text.Substring(0, i);
if (i + deleteContent.Length < p_text.Length)
sub_text += p_text.Substring(i + deleteContent.Length);
return sub_text;
}
}
}
}
return p_text;
}
private string UndoDelete(string p_text)
{
string v_text = p_text;
if (count == 0 || indexs[count - 1] > v_text.Length) return v_text;
else if (indexs[count - 1] == v_text.Length) v_text += deleteContent;
else v_text = v_text.Insert(indexs[count - 1], deleteContent);
indexs.RemoveAt(count - 1);
count--;
return v_text;
}
}
//文本复制命令
public class CopyCommand : ITextCommand
{
private string copyContent;
private static List<int> indexs = new List<int>();
private static int count = 0;
private static CSFParameter[] parameters;
private static CommandType commandType;
private bool isDefine;
private int id;
public CopyCommand(int p_id)
{
isDefine = true;
id = p_id;
commandType = CommandType.CopyCommand;
}
public CopyCommand(string p_copyContent, int p_id)
{
isDefine = false;
copyContent = p_copyContent;
id = p_id;
commandType = CommandType.CopyCommand;
}
public void Print()
{
string v_str = "[CopyCommand]:" + copyContent + " [";
for (int i = 0; i < indexs.Count; i++)
{
if (i < indexs.Count - 1) v_str += indexs[i] + ",";
else v_str += indexs[i];
}
v_str += "] " + count + " " + isDefine + " " + id;
Console.WriteLine(v_str);
}
public bool Do(ref string p_text)
{
if (isDefine) copyContent = p_text;
if (Check(p_text)) p_text = Copy(p_text);
return true;
}
public bool Undo(ref string p_text)
{
p_text = UndoCopy(p_text);
return true;
}
public int GetId()
{
return id;
}
public void SaveCommand(CommandChain p_commandChain)
{
CSFParameter p1 = new CSFParameter("copyContent", DataType.String, copyContent);
CSFParameter p2 = new CSFParameter("indexs", DataType.IntArray, indexs.ToArray());
CSFParameter p3 = new CSFParameter("count", DataType.Int, count);
CSFParameter p4 = new CSFParameter("isDefine", DataType.Boolean, isDefine);
CSFParameter p5 = new CSFParameter("id", DataType.Int, id);
CSFParameter p6 = new CSFParameter("commandType", DataType.String, commandType.ToString());
parameters = new CSFParameter[] { p1, p2, p3, p4, p5, p6 };
p_commandChain.SaveCommand(parameters);
}
public void LoadCommand(CommandChain p_commandChain)
{
CSFParameter[] v_parameters;
p_commandChain.LoadCommand(out v_parameters, this);
if (v_parameters != null)
{
foreach (CSFParameter p in v_parameters)
{
switch (p.parameterName)
{
case "copyContent":
copyContent = ((JsonElement)p.value).GetString();
break;
case "indexs":
indexs.Clear();
JsonElement v_array = (JsonElement)p.value;
for (int i = 0; i < v_array.GetArrayLength(); i++)
{
indexs.Add(v_array[i].GetInt32());
}
break;
case "count":
count = ((JsonElement)p.value).GetInt32();
break;
case "isDefine":
isDefine = ((JsonElement)p.value).GetBoolean();
break;
case "id":
id = ((JsonElement)p.value).GetInt32();
break;
case "commandType":
CommandType v_commandType;
if (Enum.TryParse<CommandType>(((JsonElement)p.value).GetString(), false, out v_commandType))
commandType = v_commandType;
break;
default: break;
}
}
}
}
public CommandType GetCommandType()
{
return commandType;
}
private string Copy(string p_text)
{
if (indexs.Count == 0 && p_text.Length == 0) indexs.Add(0);
else indexs.Add(p_text.Length);
count++;
return p_text + copyContent;
}
private string UndoCopy(string p_text)
{
string v_text = p_text;
if (count == 0 || indexs[count - 1] > v_text.Length) return v_text;
else if (indexs[count - 1] == 0) v_text = "";
else v_text = v_text.Substring(0, indexs[count - 1]);
indexs.RemoveAt(count - 1);
count--;
return v_text;
}
private bool Check(string p_text)
{
if (copyContent.Length > p_text.Length) return false;
for (int i = 0; i < p_text.Length; i++)
{
if (p_text[i] == copyContent[0] && i + copyContent.Length - 1 < p_text.Length)
{
string v_text = p_text.Substring(i, copyContent.Length);
if (v_text.Equals(copyContent)) return true;
}
}
return false;
}
}
//CommandChain是一条命令责任链,负责命令的管理、执行
public class CommandChain : TextChain, ICommandChainSave
{
public List<CSFParameter[]> parameters = new List<CSFParameter[]>();
public override void Add(ITextCommand p_command)
{
commands.Add(p_command);
}
public override void Do(ref string p_text)
{
if (index >= -1 && index < commands.Count)
commands[++index].Do(ref p_text);
}
public void Reset()
{
index = commands.Count - 1;
}
public void LoadCommand(out CSFParameter[] p_parameters, ITextCommand p_command)
{
int v_index = p_command.GetId();
p_parameters = null;
foreach (CSFParameter[] param in parameters)
{
bool isGet = false;
foreach (CSFParameter p in param)
{
if (p.parameterName.Equals("id") && ((JsonElement)(p.value)).GetInt32() == v_index)
{
p_parameters = param;
isGet = true;
break;
}
}
if (isGet) break;
}
}
public void SaveCommand(CSFParameter[] p_parameters)
{
parameters.Add(p_parameters);
}
public override void Undo(ref string p_text, IDManager p_idManager)
{
if (index > -1 && index < commands.Count)
{
commands[index].Undo(ref p_text);
commands.RemoveAt(index);
p_idManager.ReturnIndex();
index--;
}
}
}
//用于关联文本操作命令顺序的ID管理器,每个命令有唯一的ID
public class IDManager
{
private int index;
private int startIndex;
public IDManager() { startIndex = 0; index = startIndex; }
public IDManager(int p_startIndex) { startIndex = p_startIndex; index = startIndex; }
public int GetIndex()
{
return index++;
}
public void ReturnIndex()
{
index--;
}
public void ResetIndex()
{
index = startIndex;
}
}
//每个CommandChain都需要继承该类
public abstract class TextChain
{
protected int index = -1;
public List<ITextCommand> commands = new List<ITextCommand>();
public abstract void Add(ITextCommand p_command);
public abstract void Do(ref string p_text);
public abstract void Undo(ref string p_text, IDManager p_idManager);
}
//每个文本操作命令都需要实现该接口
public interface ITextCommand : ITextCommandSave
{
public bool Do(ref string p_text);
public bool Undo(ref string p_text);
public int GetId();
public CommandType GetCommandType();
public void Print();
}
//可以被存储和加载的文本操作命令实现该接口
public interface ITextCommandSave
{
public void SaveCommand(CommandChain p_commandChain);
public void LoadCommand(CommandChain p_commandChain);
}
//具备文本操作命令存储和加载功能的CommandChain实现该接口
public interface ICommandChainSave
{
public void SaveCommand(CSFParameter[] p_parameters);
public void LoadCommand(out CSFParameter[] p_parameters, ITextCommand p_command);
}
}
代码解说
我们这里对文本的添加、删除、复制进行了一个简单的模拟,所以我们这里存在Add、Delete、Copy三个命令,三个命令都需要有一个Do和Undo方法,所以我们让三个命令实现ITextCommand接口,当我们需要添加文本时就调用AddCommand,删除文本时就调用DeleteCommand,复制文本时就调用CopyCommand,加入了链式编程的实现,这使得文本编辑更加简单,同时还加入了撤销操作,为了方便命令的管理,引入了CommandChain,这类似于责任链,但是不同的是我们需要手动调用命令而不是按顺序一次性全部调用,为此每种命令都有一个静态的索引列表和记录索引个数的变量,这使得我们在进行撤销时能够按照操作顺序的相反顺序进行回溯,CommandChain对所有类型的命令的操作进行封装,所以在Text类中我们是通过CommandChain来对命令进行管理和操作的。
如果这篇文章对你有帮助,请给作者点个赞吧!