设计模式(20)- 解释器模式

解释器模式

1.定义

     给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

2.示例代码

    以解析xml为例,说明解释器模式。

   

/*要解析的xml示例文件*/
<?xml version="1.0" encoding="UTF-8">
<root id="rootId">
    <a>
         <b>
               <c name="testC">123456</c>
               <d id="1">d1</d>
               <d id="2">d2</d>
               <d id="3">d3</d>
               <d id="4">d4</d>
         </b>
    </a>
</root>
/*用于处理自定义xml取值表达式的接口*/
public abstract class ReadXmlExpression{
    /*解释表达式*/
    public abstract String[] interpret(Context c);
}

/*上下文,用来包含解释器需要的一些全局信息*/
public class Context {

    /*Dom解析Xml的Document对象*/

    private Document document = null;

    /*上一次被处理的多个元素 */

    private List<Element> preEles = new ArrayList<Element>();

    /*构造方法 需要读取的xml的路径和名字*/
    public Context(String filePathName) throws Exception{

       //通过辅助的Xml工具类来获取被解析的xml对应的Document对象
       this.document = XmlUtil.getRoot(filePathName);

    }

    /*重新初始化上下文*/
    public void reInit(){
       preEles = new ArrayList<Element>();
    }
    /*各个Expression公共使用的方法,根据父元素和当前元素的名称来获取当前的多个元素的集合*/
    public List<Element> getNowEles(Element pEle,String eleName){
       List<Element> elements = new ArrayList<Element>();
       NodeList tempNodeList = pEle.getChildNodes();
       for(int i=0;i<tempNodeList.getLength();i++){
           if(tempNodeList.item(i) instanceof Element){
              Element nowEle = (Element)tempNodeList.item(i);
              if(nowEle.getTagName().equals(eleName)){
                  elements.add(nowEle);
              }
           }
       }
       return elements;
    }

    public Document getDocument() {
       return document;
    }
    public List<Element> getPreEles() {
       return preEles;
    }
    public void setPreEles(List<Element> nowEles) {
       this.preEles = nowEles;
    }
}

/*单个元素作为非终结符的解释器*/
public class ElementExpression extends ReadXmlExpression{
    /*用来记录组合的ReadXmlExpression元素*/
    private Collection<ReadXmlExpression> eles = new ArrayList<ReadXmlExpression>();
    /* 元素的名称*/
    private String eleName = "";
    public ElementExpression(String eleName){
       this.eleName = eleName;
    }
    public boolean addEle(ReadXmlExpression ele){
       this.eles.add(ele);
       return true;
    }
    public boolean removeEle(ReadXmlExpression ele){
       this.eles.remove(ele);
       return true;
    }  
    public String[] interpret(Context c) {
       //先取出上下文里的父级元素
       List<Element> pEles = c.getPreEles();
       Element ele = null;
       //把当前获取的元素放到上下文里面
       List<Element> nowEles = new ArrayList<Element>();      
       if(pEles.size()==0){
           //说明现在获取的是根元素
           ele = c.getDocument().getDocumentElement();
           pEles.add(ele);
           c.setPreEles(pEles);
       }else{
           for(Element tempEle : pEles){
              nowEles.addAll(c.getNowEles(tempEle, eleName));
              if(nowEles.size()>0){
                  //找到一个就停止
                  break;
              }
           }
           List<Element> tempList = new ArrayList<Element>();
           tempList.add(nowEles.get(0));
           c.setPreEles(tempList);
       }   
       //循环调用子元素的interpret方法
       String [] ss = null;
       for(ReadXmlExpression tempEle : eles){
           ss = tempEle.interpret(c);
       }
       return ss;
    }
}

/* 元素作为终结符对应的解释器*/
public class ElementTerminalExpression  extends ReadXmlExpression{
    /* 元素的名字*/
    private String eleName = "";
    public ElementTerminalExpression(String name){
       this.eleName = name;
    }  
    public String[] interpret(Context c) {
       //先取出上下文里的当前元素作为父级元素
       List<Element> pEles = c.getPreEles();
       //查找到当前元素名称所对应的xml元素
       Element ele = null;
       if(pEles.size() == 0){
           //说明现在获取的是根元素
           ele = c.getDocument().getDocumentElement();
       }else{
           //获取当前的元素
           ele = c.getNowEles(pEles.get(0), eleName).get(0);
       } 
       //然后需要去获取这个元素的值
       String[] ss = new String[1];
       ss[0] = ele.getFirstChild().getNodeValue();
       return ss;
    }
}

/* 以多个元素的属性做为终结符的解释处理对象*/
public class PropertysTerminalExpression extends ReadXmlExpression{
    /* 属性名字*/
    private String propName;
    public PropertysTerminalExpression(String propName){
       this.propName = propName;
    }  
    public String[] interpret(Context c) {
       //获取最后的多个元素
       List<Element> eles = c.getPreEles();     
       String[] ss = new String[eles.size()];
       //循环多个元素,获取每个的属性的值
       for(int i=0;i<ss.length;i++){
           ss[i] = eles.get(i).getAttribute(this.propName);
       }
       return ss;
    }
}

/* 以多个元素作为终结符的解释处理对象*/
public class ElementsTerminalExpression  extends ReadXmlExpression{
    /*元素的名称*/
    private String eleName = "";
    public ElementsTerminalExpression(String name){
       this.eleName = name;
    }   
    public String[] interpret(Context c) {
       //先取出上下文里的父级元素
       List<Element> pEles = c.getPreEles();
       //获取当前的多个元素
       List<Element> nowEles = new ArrayList<Element>();      
       for(Element ele : pEles){
           nowEles.addAll(c.getNowEles(ele, eleName));
       } 
       //然后需要去获取这些元素的值
       String[] ss = new String[nowEles.size()];
       for(int i=0;i<ss.length;i++){
           ss[i] = nowEles.get(i).getFirstChild().getNodeValue();
       }
       return ss;
    }
}

/*多个元素做为非终结符的解释处理对象*/
public class ElementsExpression extends ReadXmlExpression{
    /* 用来记录组合的ReadXmlExpression元素*/
    private Collection<ReadXmlExpression> eles = new ArrayList<ReadXmlExpression>();
    /*元素名字*/
    private String eleName = "";
    public ElementsExpression(String eleName){
       this.eleName = eleName;
    }  
    public String[] interpret(Context c) {
       //先取出上下文里的父级元素
       List<Element> pEles = c.getPreEles();
       //把当前获取的元素放到上下文里面,这次是获取多个元素
       List<Element> nowEles = new ArrayList<Element>();     
       for(Element ele : pEles){
           nowEles.addAll(c.getNowEles(ele, eleName));
       }
       c.setPreEles(nowEles);     
       //循环调用子元素的interpret方法
       String [] ss = null;
       for(ReadXmlExpression ele : eles){
           ss = ele.interpret(c);
       }
       return ss;
    }  
    public boolean addEle(ReadXmlExpression ele){
       this.eles.add(ele);
       return true;
    }
    public boolean removeEle(ReadXmlExpression ele){
       this.eles.remove(ele);
       return true;
    }
}

  

/*测试获取多个元素的值的情况*/
public class Client {
    public static void main(String[] args) throws Exception {
       //准备上下文
       Context c = new Context("InterpreterTest.xml");
       //想要获取多个d元素的值,也就是如下表达式的值:"root/a/b/d$"
       //首先要构建解释器的抽象语法树
       ElementExpression root = new ElementExpression("root");
       ElementExpression aEle = new ElementExpression("a");
       ElementExpression bEle = new ElementExpression("b");
       ElementsTerminalExpression dEle = new ElementsTerminalExpression("d");
       //组合起来
       root.addEle(aEle);
       aEle.addEle(bEle);
       bEle.addEle(dEle);      
       //调用
       String ss[] = root.interpret(c);
       for(String s : ss){
           System.out.println("d的值是="+s);  
       }
    }
}

/*测试一下获取多个属性值的情况*/
public class Client {
    public static void main(String[] args) throws Exception {
       //准备上下文
       Context c = new Context("InterpreterTest.xml");
       //想要获取d元素的id属性,也就是如下表达式的值:"a/b/d$.id$"
       //首先要构建解释器的抽象语法树
       ElementExpression root = new ElementExpression("root");
       ElementExpression aEle = new ElementExpression("a");
       ElementExpression bEle = new ElementExpression("b");
       ElementsExpression dEle = new ElementsExpression("d");
       PropertysTerminalExpression prop =
       new PropertysTerminalExpression("id");
       //组合
       root.addEle(aEle);
       aEle.addEle(bEle);
       bEle.addEle(dEle);
       dEle.addEle(prop);
       //调用
       String ss[] = root.interpret(c);
       for (String s : ss) {
           System.out.println("d的属性id值是=" + s);
       }
    }
}

   

/*用来封装每一个解析出来的元素对应的属性*/
public class ParserModel {
    /* 是否单个值*/
    private boolean singleVlaue;
    /*是否属性,不是属性就是元素*/
    private boolean propertyValue;
    /*是否终结符*/
    private boolean end;
    public boolean isEnd() {
       return end;
    }
    public void setEnd(boolean end) {
       this.end = end;
    }
    public boolean isSingleVlaue() {
       return singleVlaue;
    }
    public void setSingleVlaue(boolean oneVlaue) {
       this.singleVlaue = oneVlaue;
    }
    public boolean isPropertyValue() {
       return propertyValue;
    }
    public void setPropertyValue(boolean propertyValue) {
       this.propertyValue = propertyValue;
    }
}

/* 根据语法来解析表达式,转换成为相应的抽象语法树*/
public class Parser {
    /*私有化构造器,避免外部无谓的创建对象实例*/
    private Parser(){
    }
    //定义几个常量,内部使用
    private final static String BACKLASH = "/";
    private final static String DOT = ".";
    private final static String DOLLAR = "$";
    /*按照分解的先后记录需要解析的元素的名称*/
    private static List<String> listEle = null;
    /*传入一个字符串表达式,通过解析,组合成为一个抽象的语法树*/
    public static ReadXmlExpression parse(String expr){
       //先初始化记录需解析的元素的名称的集 会
       listEle = new ArrayList<String>();
       //第一步:分解表达式,得到需要解析的元素名称和该元素对应的解析模型
       Map<String,ParserModel> mapPath = parseMapPath(expr);      
       //第二步:根据节点的属性转换成为相应的解释器对象
       List<ReadXmlExpression> list = mapPath2Interpreter(mapPath);
       //第三步:组合抽象语法树,一定要按照先后顺序来组合,
       //否则对象的包含关系就乱了
       ReadXmlExpression returnRe = buildTree(list);
       return returnRe;        
    }
/*----------------------开始实现第一步-----------------------*/
    /* 按照从左到右顺序来分解表达式,得到需要解析的元素名称,
     * 还有该元素对应的解析模型
     * @param expr 需要分解的表达式
     * @return 得到需要解析的元素名称,还有该元素对应的解析模型
     */
    private static Map<String,ParserModel> parseMapPath(String expr){
       //先按照/分割字符串
       StringTokenizer tokenizer = new StringTokenizer(expr, BACKLASH);
       //初始化一个map用来存放分解出来的值
       Map<String,ParserModel> mapPath = new HashMap<String,ParserModel>();
       while (tokenizer.hasMoreTokens()) {
           String onePath = tokenizer.nextToken();
           if (tokenizer.hasMoreTokens()) {
              //还有下一个值,说明这不是最后一个元素
              //按照现在的语法,属性必然在最后,因此也不是属性
              setParsePath(false,onePath,false,mapPath);
           } else {
              //说明到最后了
              int dotIndex = onePath.indexOf(DOT);
              if (dotIndex > 0) {
                  //说明是要获取属性的值,那就按照"."来分割,
                  //前面的就是元素名字,后面的是属性的名字
                  String eleName = onePath.substring(0, dotIndex);
                  String propName = onePath.substring(dotIndex + 1);
                  //设置属性前面的那个元素,自然不是最后一个,也不是属性
                  setParsePath(false,eleName,false,mapPath);
                  //设置属性,按照现在的语法定义,属性只能是最后一个
                  setParsePath(true,propName,true,mapPath);
              } else {
                  //说明是取元素的值,而且是最后一个元素的值
                  setParsePath(true,onePath,false,mapPath);
              }
              break;
           }
       }
       return mapPath;
    }
    /*按照分解出来的位置和名称来设置需要解析的元素名称,
     * 还有该元素对应的解析模型
     * @param end 是否是最后一个
     * @param ele 元素名称
     * @param propertyValue 是否是取属性
     * @param mapPath 设置需要解析的元素名称,还有该元素对应的解析模型的Map
     */
    private static void setParsePath(boolean end,String ele ,boolean propertyValue,Map<String,ParserModel> mapPath){
       ParserModel pm = new ParserModel();
       pm.setEnd(end);
       //如果带有$符号就说明不是一个值
       pm.setSingleVlaue(!(ele.indexOf(DOLLAR)>0));
       pm.setPropertyValue(propertyValue);             
       //去掉$
       ele = ele.replace(DOLLAR, "");
       mapPath.put(ele,pm);
       listEle.add(ele);
    }
/*----------------------第一步实现结束-----------------------*/

/*----------------------开始实现第二步-----------------------*/  
    /* 把分解出来的元素名称,根据对应的解析模型转换成为相应的解释器对象
     * @param mapPath 分解出来的需解析的元素名称,还有该元素对应的解析模型
     * @return 把每个元素转换成为相应的解释器对象后的集合
     */
    private static List<ReadXmlExpression> mapPath2Interpreter(Map<String,ParserModel> mapPath){
       List<ReadXmlExpression> list = new ArrayList<ReadXmlExpression>();
       //一定要按照分解的先后顺序来转换成解释器对象
       for(String key : listEle){
           ParserModel pm = mapPath.get(key);
           ReadXmlExpression obj = null;
           if(!pm.isEnd()){
              if(pm.isSingleVlaue()){
                  //不是最后一个,是一个值,转化为
                  obj = new ElementExpression(key);            
              }else{
                  //不是最后一个,是多个值,转化为
                  obj = new ElementsExpression(key);
              }
           }else{
              if(pm.isPropertyValue()){
                  if(pm.isSingleVlaue()){
                     //是最后一个,是一个值,取属性的值,转化为
                     obj = new PropertyTerminalExpression(key);
                  }else{
                     //是最后一个,是多个值,取属性的值,转化为
                     obj = new PropertysTerminalExpression(key);
                  }
              }else{
                  if(pm.isSingleVlaue()){
                     //是最后一个,是一个值,取元素的值,转化为
                     obj = new ElementTerminalExpression(key);
                  }else{
                     //是最后一个,是多个值,取元素的值,转化为
                     obj = new ElementsTerminalExpression(key);
                  }
              }
           }
           //把转换后的对象添加到集合中
           list.add(obj);
       }
        return list;
    }
/*----------------------第二步实现结束-----------------------*/  

/*----------------------开始实现第三步-----------------------*/  
    private static ReadXmlExpression buildTree(List<ReadXmlExpression> list){
       //第一个对象,也是返回去的对象,就是抽象语法树的根
       ReadXmlExpression returnRe = null;
       //定义上一个对象
       ReadXmlExpression preRe = null;
       for(ReadXmlExpression re : list){        
           if(preRe==null){
              //说明是第一个元素
              preRe = re;
              returnRe = re;
           }else{
              //把元素添加到上一个对象下面,同时把本对象设置成为oldRe,
              //作为下一个对象的父结点
              if(preRe instanceof ElementExpression){
                  ElementExpression ele = (ElementExpression)preRe;
                  ele.addEle(re);
                  preRe = re;
              }else if(preRe instanceof ElementsExpression){
                  ElementsExpression eles = (ElementsExpression)preRe;
                  eles.addEle(re);
                  preRe = re;
              }
           }
       }
       return returnRe;
    }
/*----------------------第三步实现结束-----------------------*/
}

   

/* 使用解析器
*想要获取c元素的值,表达式为:“root/a/b/c”
*想要获取c元素的name属性值,表达式为:“root/a/b/c.name”
*想要获取d元素的值,表达式为:“root/a/b/d$”,获取d的属性测试
*/
public class Client {
    public static void main(String[] args) throws Exception {
       //准备上下文
       Context c = new Context("InterpreterTest.xml");
       //通过解析器获取抽象语法树
       ReadXmlExpression re = Parser.parse("root/a/b/d$.id$");
       //请求解析,获取返回值
       String ss[] = re.interpret(c);
       for (String s : ss) {
           System.out.println("d的属性id值是=" + s);
       }   
       //如果要使用同一个上下文,连续进行解析,需要重新初始化上下文对象
       c.reInit();
       ReadXmlExpression re2 = Parser.parse("root/a/b/d$");
       //请求解析,获取返回值
       String ss2[] = re2.interpret(c);
       for (String s : ss2) {
           System.out.println("d的值是=" + s);
       }
    }
}

   

3.实际应用

       解释器模式通过一个解释器对象处理一个语法规则的方式,把复杂的功能分离开;然后选择需要被执行的功能,并把这些功能组合成为需要被解释执行的抽象语法树;然后再按照抽象语法树来解释执行,实现相应的功能。

解释器模式的本质:分离实现,解释执行

猜你喜欢

转载自renhanxiang.iteye.com/blog/2408376