C#中使用简单工厂模式实现命令解释器

来源:互联网 发布:java二级考试题库 编辑:程序博客网 时间:2024/05/29 03:49

 

 

    现假设需要实现一个简单的算术计算解释器,在这个算术计算解释器中,用户通过使用add、subtract、multiply和divide命令分别获得两个无符号整形参数(非负整数)的和、差、积以及商。传统的做法是从标准输入或文件逐条地读入命令,并由专门的语法和语义解析程序对每条命令进行语法和语义分析,然后根据命令的语义对参数进行计算并输出结果。这种方法的优点在于实现思路简单,设计的核心就在于这个专门的语法和语义解析程序,然而这并不是一个好的做法,因为程序的扩充性不强,当我们需要向解释器中添加一个新的命令时,我们需要修改语法语义解析程序。
在C#中使用简单工厂模式就可以很好地解决这个问题。首先我们回顾一下简单工厂模式。简单工厂模式是一种类的创建型模式,它可以通过向工厂类传递参数来创建所需产品类的实例。在简单工厂模式中,所有的产品类都从同一个接口继承,并实现接口中的方法,工厂类在获得参数后,根据参数的取值来创建不同产品类的实例。下图简要地描述了简单工厂模式的基本结构。


 

图一 简单工厂模式类示意图

    在上图中,产品类Product1和Product2继承了IProduct接口,产品工厂类ProductFactory的CreateProduct方法用于根据外部传入的参数来生成相应产品类的实例,用户类UserClass通过使用工厂类ProductFactory的CreateProduct方法来获得该实例。
    现在再来讨论算术计算解释器的设计方法。我们可以将解释器所要解释的命令看成是对象,那么解释器执行命令的过程就是创建对象并执行对象中特定操作的过程。通过分析不难发现,对于一个命令而言,最主要的就是能够正确地分析该命令的语法并执行该命令,当然,更多地为用户考虑,我们可以再多定义一个help命令,用于获得其它命令的帮助信息。
    首先我们必须通过分析来得到这样的信息,对于这个算术计算解释器中的命令对象,它们有哪些相同的操作,以便于确定工厂模式的产品接口(即IProduct接口)中应该包括哪些方法的声明。在确定了这个产品接口后,其它的命令类只要继承该产品接口,就可以被工厂类(即ProductFactory类)实例化,进而通过执行实例中的接口方法来达到执行命令的目的。同样,当需要向解释器中添加新的命令时,只需要创建一个新的继承于产品接口的类,并实现产品接口中预先定义的方法即可。
    在解释器产品接口的所有方法中,最为重要的是命令的执行方法,该方法决定了命令的执行逻辑(也就是确定当前命令的输入是什么,将得到什么样的输出),这一点是显而易见的。在上面的分析中我们提到,需要再提供一个help命令,用于显示各命令的帮助信息,因此,解释器中的每条命令还需要有一个GetHelpText方法,用来返回该命令的帮助文本。综上所述,解释器产品接口中至少要有两个方法:Execute方法和GetHelpText方法。在C#中,我们可以使用下面的代码来定义这个产品接口。
using System;

namespace SimpleCalc.ICalc
{
 ///


 /// 算术计算解释器产品接口(IProduct)
 ///
 public interface ICalcCommand
 {
  ///
  /// 命令执行方法
  ///
  /// 命令的参数
  /// 命令执行结果
  int Execute(string[] _parms);
  ///
  /// 用于返回命令的帮助文本信息
  ///
  /// 命令的帮助信息
  string GetHelpText();
 }
}
    在确定了命令解释器的产品接口后,我们暂不讨论各条命令产品类的具体实现方法,先来看看产品工厂类的实现。对于命令解释器的产品工厂类,它需要从外界获得一个命令名称的参数,用于标识外界需要工厂类实例化哪个产品类。在C#中,可以通过这个参数构造出类的长格式名称,然后使用Reflection技术生成指定类的实例。下面的C#代码实现了命令解释器的产品工厂类。
using System;
using System.Reflection;
using SimpleCalc.ICalc;

namespace SimpleCalc.Main
{
 ///


 /// 命令解释器的产品工厂抽象类
 ///
 public abstract class CommandFactory
 {
  public static ICalcCommand GetCommandInstance(string _cmdName)
  {
   try
   {
    string path   = "Commands";
    string className = "SimpleCalc.Commands." + _cmdName.ToUpper();
    return (ICalcCommand) Assembly.Load(path).CreateInstance(className);
   }
   catch
   {
    throw new Exception("Unable to load this command!/n");
   }
  }
 }
}
    在上面的代码中,CommandFactory类被定义成抽象类,因为该类只有一个GetCommandInstance静态方法,在实际的应用中无需对类进行实例化。
    在定义了工厂类后,命令的执行过程就变得很简单,只需要调用CommandFactory.GetCommandInstance方法获得命令产品类的实例,然后调用该实例的Execute方法就可以了,例如:
string[] parms = cmd.Split(' ');
ICalcCommand instance = CommandFactory.GetCommandInstance(parms[0]);
if (null == instance)
{
 Console.WriteLine("Unknown command!/n");
 // 。。。
}
int result = instance.Execute(parms);
if (result >= 0)
{
 Console.WriteLine(string.Format("{0}/n", result));
}
    现在再来看加法命令(add命令)的实现过程。我们新创建一个名为ADD的类,并使该类继承于ICalcCommand接口,然后分别实现该接口的Execute方法和GetHelpText方法:
using System;
using SimpleCalc.ICalc;

namespace SimpleCalc.Commands
{
 ///


 /// 加法命令产品类
 ///
 public class ADD : ICalcCommand
 {
  public ADD()
  {
  }
  #region ICalcCommand Members
  public string GetHelpText()
  {
   string txt = "/n";
   txt += "Usage: ADD ";
   return txt;
  }

  public int Execute(string[] _parms)
  {
   uint x, y;
   // 参数格式的判断(ADD命令定义为三个参数,ADD命令本身以及两个非负整数)
   if (_parms.Length != 3)
   {
throw new Exception(string.Format("This command does not take {0} parameters!", _parms.Length - 1));
   }
   try
   {
    x = Convert.ToUInt32(_parms[1]);
    y = Convert.ToUInt32(_parms[2]);
   }
   catch (System.FormatException ex)
   {
    throw new Exception(ex.Message);
   }
   return (int)(x + y);
  }
  #endregion
 }
}
    上面的代码中使用了大写的ADD来定义这个类,这是由工厂类中的产品实例化方法(GetCommandInstance方法)所决定的,在该方法中,我们对输入的参数_cmdName执行了ToUpper()操作,以便无论用户输入的命令是“add”还是“Add”,产品实例化方法都能够正确地创建该命令类的实例。与此相似,我们可以向解释器中继续添加subtract、multiply和divide命令。
    最后一个问题,就是如何实现help命令。help命令和add、substract等命令一样,也是被命令解释器所支持的一条命令,因此它也要继承于ICalcCommand接口。在help命令类的Execute方法中,我们需要根据help命令的参数来创建需要获得帮助的命令的实例,并调用实例化对象的GetHelpText方法以显示帮助信息。下面的代码显示了help命令类的Execute方法是如何获得指定命令的帮助信息并将其显示出来的。
public int Execute(string[] _parms)
{
 // 参数判断
 if (_parms.Length != 2)
 {
  throw new Exception(string.Format("This command does not take {0} parameters!", _parms.Length - 1));
 }
 try
 {
  // 获得指定的命令名称
  string cmd = _parms[1];
  // 创建该命令的实例
  ICalcCommand instance = CommandFactory.GetCommandInstance(cmd);
  if (null == instance)
  {
   throw new Exception("Unknown command!");
  }
  // 调用实例的GetHelpText方法并将结果显示出来
  Console.WriteLine(instance.GetHelpText());
 }
 catch (Exception ex)
 {
  throw ex;
 }
 return -1;
}
    下图是已经完成的无符号整数算术计算解释器的运行画面,在命令行中,我们可以方便地使用各种命令来获得需要的计算结果,并可以使用help命令来获得某条命令的帮助信息。

原创粉丝点击