12.行为型模式-解释器模式
12.1 基础知识
学习难度:5
使用频率:1
总分:2
定义
解释器模式(Interpreter Pattern)给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
说人话
解释器模式用于定义一种语言的文法,并提供解释器来解释语言中的句子。在解释器模式中,非终结表达式的解释方法调用其他表达式的解释方法进行操作。
结构图
实现步骤
- 上下文类:包含需要解释的信息或一些解释信息相关的集合,并提供操作方法以供解释器使用。
- 表达式接口:定义解释方法。
- 终结符表达式类:实现抽象表达式接口,表示文法中的终结符。实现解释方法时会返回不可再操作的表达式。
- 非终结符表达式类:实现抽象表达式接口,表示文法中的非终结符。通常会在类中定义其他表达式变量。实现解释方法时会对其他表达式调用解释方法并进行操作。
- 客户端:创建上下文类,定义表达式树得到表达式对象并解释。
说明
解释器模式在实际应用中使用频率较低,通常用于特定领域的语言解析和处理,例如编译器、正则表达式解析等。
12.2 模版代码
上下文类
// 上下文类 包含要被解释的信息
class Context
{
public string Input { get; set; }
public string Output { get; set; }
public Context(string input)
{
Input = input;
}
}
表达式接口和终结非总结表达式类
// 表达式接口 定义了解释方法
interface IExpression
{
void Interpret(Context context);
}
// 终结符表达式
class TerminalExpression : IExpression
{
public void Interpret(Context context)
{
// 实现终结符表达式的解释逻辑
context.Output = "终结符表达式";
}
}
// 非终结符表达式类
class NonTerminalExpression : IExpression
{
private IExpression leftExpression;
private IExpression rightExpression;
public NonTerminalExpression(IExpression left, IExpression right)
{
leftExpression = left;
rightExpression = right;
}
public void Interpret(Context context)
{
// 实现非终结符表达式的解释逻辑,通常基于左右表达式的组合
leftExpression.Interpret(context);
rightExpression.Interpret(context);
context.Output = $"非终结符表达式 [{leftExpression.GetType().Name} {rightExpression.GetType().Name}]";
}
}
客户端
class Program
{
static void Main(string[] args)
{
Context context = new Context("示例输入");
// 创建两个终结符表达式和一个非终结符表达式
IExpression terminalExpression1 = new TerminalExpression();
IExpression terminalExpression2 = new TerminalExpression();
IExpression nonTerminalExpression = new NonTerminalExpression(terminalExpression1, terminalExpression2);
// 解释表达式
nonTerminalExpression.Interpret(context);
Console.WriteLine(context.Output);//非终结符表达式 [TerminalExpression TerminalExpression]
}
}
12.3 CSharp实践
实践需求
使用解释器模式,模拟加减运算。
解释器接口和具体解释器类
// 抽象表达式接口
public interface IExpression
{
int Interpret(Dictionary<string, int> variables);
}
// 终结符表达式类 - 变量表达式
public class VariableExpression : IExpression
{
private string variableName;
public VariableExpression(string name)
{
variableName = name;
}
public int Interpret(Dictionary<string, int> variables)
{
if (variables.ContainsKey(variableName))
{
return variables[variableName];
}
else
{
throw new InvalidOperationException($"变量 '{variableName}' 未找到。"); // 抛出未找到变量的异常
}
}
}
// 终结符表达式类 - 常量表达式
public class ConstantExpression : IExpression
{
private int value;
public ConstantExpression(int value)
{
this.value = value;
}
public int Interpret(Dictionary<string, int> variables)
{
return value;
}
}
// 非终结符表达式类 - 加法表达式
public class AddExpression : IExpression
{
private IExpression left;
private IExpression right;
public AddExpression(IExpression left, IExpression right)
{
this.left = left;
this.right = right;
}
public int Interpret(Dictionary<string, int> variables)
{
return left.Interpret(variables) + right.Interpret(variables);
}
}
// 非终结符表达式类 - 减法表达式
public class SubtractExpression : IExpression
{
private IExpression left;
private IExpression right;
public SubtractExpression(IExpression left, IExpression right)
{
this.left = left;
this.right = right;
}
public int Interpret(Dictionary<string, int> variables)
{
return left.Interpret(variables) - right.Interpret(variables);
}
}
客户端
class Program
{
static void Main(string[] args)
{
// 创建变量字典并赋值
Dictionary<string, int> variables = new Dictionary<string, int>();
variables["x"] = 10;
variables["y"] = 5;
// 创建表达式 x+20
IExpression expression = new AddExpression(new VariableExpression("x"), new ConstantExpression(20));
// 解释并计算表达式的值
int result = expression.Interpret(variables);
Console.WriteLine("结果: " + result); // 30
// 创建另一个表达式 x+20的结果-5
expression = new SubtractExpression(expression, new VariableExpression("y"));
// 再次解释并计算表达式的值
result = expression.Interpret(variables);
Console.WriteLine("结果: " + result); // 25
}
}
12.4 Unity实践
实践需求
使用解释器模式,模拟剑圣可以单个使用技能和使用连招组合。
输入信息相关类
//易大师输入信息抽象类
public abstract class YiInputInfo
{
}
// 表示易大师的单按键输入信息类
public class YiSingleInputInfo : YiInputInfo
{
public KeyCode keyCode; // 存储单按键的 KeyCode
// 构造函数,用于初始化单按键输入信息
public YiSingleInputInfo(KeyCode keyCode)
{
this.keyCode = keyCode; // 通过构造函数接收按键信息并存储在类的成员变量中
}
}
// 表示易大师的连招输入信息类
public class YiComboInputInfo : YiInputInfo
{
public List<KeyCode> keyCodeList; // 存储连招输入的按键列表
// 构造函数,用于初始化连招输入信息
public YiComboInputInfo(List<KeyCode> keyCodeList)
{
this.keyCodeList = keyCodeList; // 通过构造函数接收按键列表信息并存储在类的成员变量中
}
}
解释器相关类
// 表示易大师技能表达式的通用接口
public interface ISkillExpression
{
// 执行技能表达式,接受一个 YiContext 参数,以便在执行中访问游戏对象和数据
void Execute(YiContext yiContext);
}
// 表示易大师的技能表达式类,用于执行“阿尔法突袭”技能
public class AlphaStrikeExpression : ISkillExpression
{
// 执行“阿尔法突袭”技能
public void Execute(YiContext yiContext)
{
Debug.Log("无极剑圣使用了阿尔法突袭,造成大量伤害!");
// 在此处可以添加执行 Alpha Strike 技能的伤害逻辑
// 使用动画触发器来播放技能动画
yiContext.yiAnimator.SetTrigger(yiContext.GetSkillExpressionKeyCode(this.GetType()).ToString());
}
}
// 表示易大师的技能表达式类,用于执行“冥想”技能
public class MeditationExpression : ISkillExpression
{
// 执行“冥想”技能
public void Execute(YiContext yiContext)
{
Debug.Log("无极剑圣开始冥想,恢复生命值!");
// 在此处可以添加执行 Meditation 技能的生命值恢复逻辑
// 使用动画触发器来播放技能动画
yiContext.yiAnimator.SetTrigger(yiContext.GetSkillExpressionKeyCode(this.GetType()).ToString());
}
}
// 表示易大师的技能表达式类,用于执行“无极剑道”技能
public class WujuStyleExpression : ISkillExpression
{
// 执行“无极剑道”技能
public void Execute(YiContext yiContext)
{
Debug.Log("无极剑圣启动了无极剑道,攻击变成真实伤害!");
// 在此处可以添加执行 Wuju Style 技能的效果逻辑,例如将攻击变成真实伤害
// 使用动画触发器来播放技能动画
yiContext.yiAnimator.SetTrigger(yiContext.GetSkillExpressionKeyCode(this.GetType()).ToString());
}
}
// 表示易大师的连招技能表达式类,实现了 ISkillExpression 接口
public class ComboExpression : ISkillExpression
{
public List<ISkillExpression> comboSkillExpressionList = new List<ISkillExpression>();
// 构造函数,用于初始化连招技能表达式
public ComboExpression(List<ISkillExpression> comboSkillExpressionList)
{
this.comboSkillExpressionList = comboSkillExpressionList; // 通过构造函数接收连招技能表达式列表并存储在类的成员变量中
}
// 执行连招技能
public void Execute(YiContext yiContext)
{
Debug.Log("进入连招");
string comboKeyCodeStr = null;
// 遍历连招技能表达式列表
foreach (var comboSkillExpressionItem in comboSkillExpressionList)
{
comboKeyCodeStr += yiContext.GetSkillExpressionKeyCode(comboSkillExpressionItem.GetType()) + " ";
// comboSkillExpressionItem.Execute(yiContext);
}
// 使用协程执行连招技能
MyTimer.GetInstance().StartCoroutine(ExecuteComboSkills(comboSkillExpressionList, yiContext));
Debug.Log("连招是" + comboKeyCodeStr);
// Debug.Log("退出连招");
}
// 协程,用于逐个执行连招技能
IEnumerator ExecuteComboSkills(List<ISkillExpression> comboSkillExpressionList, YiContext yiContext)
{
// 遍历连招技能表达式列表
foreach (var comboSkillExpressionItem in comboSkillExpressionList)
{
// 执行当前连招技能表达式
comboSkillExpressionItem.Execute(yiContext);
// 等待一定数量的帧
for (int i = 0; i < 100; i++)
{
yield return null; // 等待一帧
}
}
}
}
易大师技能系统上下文类
// 易大师技能系统上下文类
public class YiContext
{
// 易大师游戏对象的预制体
private GameObject yiPrefab = Resources.Load<GameObject>("Lesson30_行为型模式_解释器模式/Yi");
// 易大师游戏对象
private GameObject yiGameObject;
// 易大师技能表达式与按键之间的映射字典
private Dictionary<KeyCode, Type> yiSkillMappingDictionary;
// 获取技能映射字典的访问器
public Dictionary<KeyCode, Type> YiSkillMappingDictionary => yiSkillMappingDictionary;
// 易大师的动画控制器
public Animator yiAnimator;
// 易大师技能系统上下文的构造函数
public YiContext()
{
// 实例化易大师游戏对象
yiGameObject = GameObject.Instantiate(yiPrefab);
// 获取易大师的动画控制器
yiAnimator = yiGameObject.GetComponent<Animator>();
// 初始化技能映射字典,并将按键与对应的技能表达式类型关联
yiSkillMappingDictionary = new Dictionary<KeyCode, Type>();
yiSkillMappingDictionary.Add(KeyCode.Q, typeof(AlphaStrikeExpression));
yiSkillMappingDictionary.Add(KeyCode.W, typeof(MeditationExpression));
yiSkillMappingDictionary.Add(KeyCode.E, typeof(WujuStyleExpression));
}
// 执行给定的技能表达式
public void ExecuteSkill(ISkillExpression skillExpression)
{
skillExpression.Execute(this);
}
// 检查易大师是否处于空闲状态
public bool IsYiIdle()
{
foreach (var yiSkillMappingItem in yiSkillMappingDictionary)
{
// 如果有技能在执行中,则易大师不是空闲的
if (yiAnimator.GetBool(yiSkillMappingItem.Key.ToString()))
{
return false;
}
}
// 所有技能都未在执行中,易大师被认为是空闲的
return true;
}
// 获取与给定技能表达式类型关联的按键
public KeyCode GetSkillExpressionKeyCode(Type skillExpressionType)
{
foreach (var yiSkillMappingItem in yiSkillMappingDictionary)
{
// 如果找到与给定技能表达式类型匹配的项,返回关联的按键
if (skillExpressionType == yiSkillMappingItem.Value)
return yiSkillMappingItem.Key;
}
// 如果没有找到匹配的项,记录错误并返回默认按键
Debug.Log("该技能没有在技能映射字典");
return KeyCode.Space;
}
}
易大师输入管理器类
// 易大师输入管理器类,用于处理技能输入和解析
public class YiInputManager : MonoBehaviorSingleton<YiInputManager>
{
// 存储易大师的输入信息列表
private List<YiInputInfo> yiInputInfoList = new List<YiInputInfo>();
// 用于临时存储连招输入信息的按键列表
private List<KeyCode> comboInputInfoKeyCodeList = new List<KeyCode>();
// 输入管理器的活动状态标志
private bool isActive;
// 易大师上下文,用于技能解析和执行
private YiContext yiContext;
// 连续输入的时间阈值
private float CONTINUOUS_INPUT_THRESHOLD = 0.2f;
// 上次输入时间
private float lastInputTime;
// 标志是否正在连续输入
private bool isInputContinuous;
// 向输入列表添加输入信息
public void AddYiInputInfo(YiInputInfo yiInputInfo)
{
yiInputInfoList.Add(yiInputInfo);
}
// 设置输入管理器的活动状态和相关上下文
public void SetActive(bool active, YiContext context)
{
this.isActive = active;
this.yiContext = context;
}
// 解析输入信息为技能表达式
public ISkillExpression ParseYiInputInfoToSkillExpression(YiInputInfo yiInputInfo)
{
// 检查输入信息的类型是否为单个按键输入
if (yiInputInfo is YiSingleInputInfo)
{
// 将输入信息转换为单个按键输入对象
YiSingleInputInfo yiSingleInputInfo = yiInputInfo as YiSingleInputInfo;
// 检查按键是否在技能映射字典中
if (yiContext.YiSkillMappingDictionary.ContainsKey(yiSingleInputInfo.keyCode))
{
// 根据按键查找对应的技能表达式类型,并创建技能表达式实例
return (ISkillExpression)Activator.CreateInstance(
yiContext.YiSkillMappingDictionary[yiSingleInputInfo.keyCode]);
}
}
// 如果输入信息的类型为连招输入
else if (yiInputInfo is YiComboInputInfo)
{
// 将输入信息转换为连招输入对象
YiComboInputInfo yiComboInputInfo = yiInputInfo as YiComboInputInfo;
List<ISkillExpression> comboSkillExpressionList = new List<ISkillExpression>();
// 遍历连招输入中的按键列表
foreach (var keyCode in yiComboInputInfo.keyCodeList)
{
// 检查按键是否在技能映射字典中
if (yiContext.YiSkillMappingDictionary.ContainsKey(keyCode))
{
// 根据按键查找对应的技能表达式类型,并创建技能表达式实例,添加到连招技能表达式列表
ISkillExpression skillExpression =
(ISkillExpression)Activator.CreateInstance(yiContext.YiSkillMappingDictionary[keyCode]);
comboSkillExpressionList.Add(skillExpression);
}
}
// 创建并返回一个连招技能表达式
return new ComboExpression(comboSkillExpressionList);
}
// 如果无法识别输入信息的类型或按键无法匹配到技能表达式,记录错误并返回空
Debug.Log("无法转换为技能表达式");
return null;
}
// 检查输入信息
public void CheckInputInfo()
{
float currentTime = Time.time;
// 如果距离上次输入时间超过连续输入的时间阈值
if (currentTime - lastInputTime > CONTINUOUS_INPUT_THRESHOLD)
{
// 如果连招输入列表不为空
if (comboInputInfoKeyCodeList.Count != 0)
{
// 如果连招输入列表只包含一个按键
if (comboInputInfoKeyCodeList.Count == 1)
{
// 创建一个单按键输入信息并添加到输入信息列表中
List<KeyCode> inputInfoKeyCodeList = comboInputInfoKeyCodeList.ToList();
AddYiInputInfo(new YiSingleInputInfo(inputInfoKeyCodeList[0]));
comboInputInfoKeyCodeList.Clear();
Debug.Log("非连续输入");
}
// 如果连招输入列表包含多个按键
else if (comboInputInfoKeyCodeList.Count > 1)
{
// 创建一个连招输入信息并添加到输入信息列表中
List<KeyCode> inputInfoKeyCodeList = comboInputInfoKeyCodeList.ToList();
AddYiInputInfo(new YiComboInputInfo(inputInfoKeyCodeList));
comboInputInfoKeyCodeList.Clear();
Debug.Log("连续输入");
}
}
}
// 如果有任何按键被按下
if (UnityEngine.Input.anyKeyDown)
{
// 遍历易大师技能映射字典中的每个键值对
foreach (var yiSKillMappingItem in yiContext.YiSkillMappingDictionary)
{
// 检查当前帧是否按下了映射字典中的某个技能按键
if (UnityEngine.Input.GetKeyDown(yiSKillMappingItem.Key))
{
currentTime = Time.time;
// 判断当前时间与上次输入时间的差值是否小于连续输入的阈值
if (currentTime - lastInputTime <= CONTINUOUS_INPUT_THRESHOLD)
{
// 表示此次输入为连续输入
isInputContinuous = true;
}
else
{
// 重置连续输入标志,表示此次输入为非连续输入
isInputContinuous = false;
}
// 处理连续输入
if (isInputContinuous)
{
Debug.Log("连续输入");
comboInputInfoKeyCodeList.Add(yiSKillMappingItem.Key);
}
// 处理非连续输入
else
{
if (comboInputInfoKeyCodeList.Count != 0)
{
// 如果连招输入列表只包含一个按键
if (comboInputInfoKeyCodeList.Count == 1)
{
Debug.Log("非连续输入");
// 创建单按键输入信息并添加到输入信息列表中
List<KeyCode> inputInfoKeyCodeList = comboInputInfoKeyCodeList.ToList();
AddYiInputInfo(new YiSingleInputInfo(inputInfoKeyCodeList[0]));
comboInputInfoKeyCodeList.Clear();
comboInputInfoKeyCodeList.Add(yiSKillMappingItem.Key);
}
// 如果连招输入列表包含多个按键
else if (comboInputInfoKeyCodeList.Count > 1)
{
Debug.Log("连续输入");
// 创建连招输入信息并添加到输入信息列表中
List<KeyCode> inputInfoKeyCodeList = comboInputInfoKeyCodeList.ToList();
AddYiInputInfo(new YiComboInputInfo(inputInfoKeyCodeList));
comboInputInfoKeyCodeList.Clear();
comboInputInfoKeyCodeList.Add(yiSKillMappingItem.Key);
}
}
else
{
// 如果连招输入列表为空,直接添加当前按键
comboInputInfoKeyCodeList.Add(yiSKillMappingItem.Key);
}
}
// 更新上次输入时间
lastInputTime = currentTime;
}
}
}
}
private void Update()
{
if (!isActive) return;
CheckInputInfo();
if (yiInputInfoList.Count > 0 && yiContext.IsYiIdle())
{
// 执行下一个技能表达式并从输入列表中移除
yiContext.ExecuteSkill(ParseYiInputInfoToSkillExpression(yiInputInfoList[0]));
yiInputInfoList.Remove(yiInputInfoList[0]);
}
}
}
客户端
public class TestInterpreterPattern:MonoBehaviour
{
void Start()
{
// 创建无极剑圣易大师
YiContext yiContext = new YiContext();
YiInputManager.GetInstance().SetActive(true,yiContext);
}
}
运行结果
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com