• Java设计模式之九 ----- 解释器模式和迭代器模式


    前言

    上一篇中我们学习了行为型模式的责任链模式(Chain of Responsibility Pattern)和命令模式(Command Pattern)。本篇则来学习下行为型模式的两个模式, 解释器模式(Interpreter Pattern)和迭代器模式(Iterator Pattern)。

    解释器模式

    简介

    解释器模式顾名思义,就是对某事物进行解释。给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器客户端可以使用这个解释器来解释这个语言中的句子

    解释器模式其实就是对某事物进行解释。比如生活中经常用到的计算器,将我们用的语言转换成计算器预言,还有我们编写代码时用到的正则表达式等等。《大话设计模式》中对这个模式有个比较有意思的讲解,其中示例就是把老板对不同人说相同的话,不同的人会理解不同。这也说明的解释器模式核心就是进行解释。

    解释器模式主要由这四个角色组成,抽象表达式(Expression)角色、终结符表达式(Terminal Expression)角色、非终结符表达式(Nonterminal Expression)角色和环境(Context)角色。

    • 抽象解释器:声明一个所有具体表达式都要实现的抽象接口(或者抽象类),接口中主要是一个interpret()方法,称为解释操作。具体解释任务由它的各个实现类来完成,具体的解释器分别由终结符解释器TerminalExpression和非终结符解释器NonterminalExpression完成。
    • 终结符表达式:实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。终结符一半是文法中的运算单元,比如有一个简单的公式R=R1+R2,在里面R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。
    • 非终结符表达式:文法中的每条规则对应于一个非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2中,+就是非终结符,解析+的解释器就是一个非终结符表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
    • 环境角色:这个角色的任务一般是用来存放文法中各个终结符所对应的具体值,比如R=R1+R2,我们给R1赋值100,给R2赋值200。这些信息需要存放到环境角色中,很多情况下我们使用Map来充当环境角色就足够了。

    这里为了方便理解,我们使用一个简单的示例来加以说明。
    平常我们在进行英语学习的时候,会自行翻译或者用到翻译工具。但是不同的翻译工具翻译的结果也可能不一样,这时我们只需要拿到自己想要的结果就行了。比如,使用百度和有道翻译“好好学习,天天向上!”,它们翻译的结果分别为“Study hard and keep up!”和“study hard and make progress every day!”,而xuwujing翻译的结果是“ Good good study, day day up!”。
    那么我们便可以用解释器模式来实现这种场景。
    首先定义一个抽象的解释器接口,有解释的这个方法,然后再定义不同的解释器实现该接口和方法,最后再来进行测试。那么代码如下:

    interface Expreeion{
       void interpert(String word);
    }
    
    class  BaiduExpreeion implements Expreeion{
       String str ="好好学习,天天向上!";
       @Override
       public void interpert(String word) {
           if(str.equals(word)) {
               System.out.println("百度翻译:"+word+" 的英文是  Study hard and keep up!");
           }
       }
    }
    
    class  YouDaoExpreeion implements Expreeion{
       String str ="好好学习,天天向上!";
       @Override
       public void interpert(String word) {
           if(str.equals(word)) {
               System.out.println("有道翻译:"+word+" 的英文是  study hard and make progress every day!");
           }
       }
    }
    
    class  XuWuJingExpreeion implements Expreeion{
       String str ="好好学习,天天向上!";
       @Override
       public void interpert(String word) {
           if(str.equals(word)) {
               System.out.println("xuwujing翻译:"+word+" 的英文是  Good good study, day day up!");
           }
       }
    }
    
    public class InterpreterTest {
       public static void main(String[] args) {
           String word = "好好学习,天天向上!";
           Expreeion expreeion =new  BaiduExpreeion();
           Expreeion expreeion2 =new  YouDaoExpreeion();
           Expreeion expreeion3 =new  XuWuJingExpreeion();
           expreeion.interpert(word);
           expreeion2.interpert(word);
           expreeion3.interpert(word);
       }
    }

    输出结果:

    
    百度翻译:好好学习,天天向上! 的英文是  Study hard and keep up!
    有道翻译:好好学习,天天向上! 的英文是  study hard and make progress every day!
    xuwujing翻译:好好学习,天天向上! 的英文是  Good good study, day day up!

    解释器模式优点:

    扩展性好,子类扩展非常方便。
    实现简单。

    解释器模式缺点:

    可使用的场景比较少;
    类过多的话,会使代码臃肿,难以维护;

    使用场景:

    一个简单的语法规则需要解释的场景,比如sql。
    有重复的问题的时候。

    迭代器模式

    简介

    迭代器模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示,属于行为型模式。 它提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。

    我们对迭代器(Iterator)肯定不陌生,因为我们在Java开发中会经常用到,比如对List、Set和Map集合进行遍历或对数组进行遍历的时候。但是迭代器模式的话,可能就不太理解了,这里我们就简单讲讲迭代器模式。

    迭代器模式主要由这四个角色组成,迭代器角色(Iterator)、具体迭代器角色(Concrete Iterator)、容器角色(Container)和具体容器角色(Concrete Container)。

    • 迭代器角色(Iterator):通过接口或抽象类声明实现的方法。
    • 具体迭代器角色(Concrete Iterator):具体迭代器角色要实现迭代器接口,并要记录遍历中的当前位置。
    • 容器角色(Container):容器角色负责提供创建具体迭代器角色的接口。
    • 具体容器角色(Concrete Container):具体容器角色实现创建具体迭代器角色的接口——这个具体迭代器角色于该容器的结构相关。

    因为迭代器我们平时用的比较多,这里也不在过多描述了,这里就简单的介绍下迭代器模式的运作。
    首先,定义一个迭代器角色(MyIterator )和容器角色(MyIterable)的接口。
    代码如下:

    interface MyIterator {
        boolean hasNext();
        String next();
    }
    
    interface MyIterable{
        MyIterator getIterator();
        void add(String str);
        String get(int index);
    }

    然后定义一个 具体容器角色(ListContainer )实现容器角色的接口,这里的实现方法通过List自带的进行实现;然后再定义一个具体迭代器角色(ListIterator )实现迭代器角色的接口,这里的实现的方法由自己实现。
    那么代码如下:

    
    class ListContainer implements MyIterable {
        
         private List<String> list =new ArrayList<>(); 
    
         
        @Override
        public MyIterator getIterator() {
            return new ListIterator();
        }
    
        @Override
        public void add(String str) {
            list.add(str);
        }
    
        @Override
        public String get(int index) {
            return list.get(index);
        }
        
        class ListIterator implements MyIterator{
            int index;
            @Override
            public boolean hasNext() {
                return index < list.size();
            }
    
            @Override
            public String next() {
                if (this.hasNext()) {
                    return list.get(index++);
                }
                return null;
            }
        }
    }
    

    最后再来进行代码的测试。
    测试代码如下:

    
    public static void main(String[] args) {
            MyIterable myIterable = new ListContainer();
            myIterable.add("1");
            myIterable.add("zhangsan");
            myIterable.add("2");
            myIterable.add("lisi");
            myIterable.add("3");
            myIterable.add("xuwujing"); 
            MyIterator myIterator = myIterable.getIterator();
            while (myIterator.hasNext()){
                String str = myIterator.next();
                System.out.println(str);
            }      
        }

    输出结果:

            1
            zhangsan
            2
            lisi
            3
            xuwujing

    迭代器模式优点:

    灵活度高,可以通过不同的方式遍历对象;
    扩展性好,可以很方便的增加新的聚合类和迭代器类而不用修改之前的代码。

    迭代器模式缺点:

    由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

    使用场景:

    需要为聚合对象提供遍历的功能的时候。

  • 相关阅读:
    c# 把对象加入队列,对象为全局变量,对象改变队列值也跟着改变
    C# 一个数组未赋值引发的错误
    c# 2016QQ自动登录程序
    当时钟事件声明为过程变量 让system.threading.timer时钟失效
    if 循环的深入理解 哈希表的一种应用
    VB6对象与地址相互转换
    VB6的函数指针传递
    .net framework 4.0 从 GAC 卸载 程序集
    .net framework 4.0 从 GAC 卸载 程序集
    GAC in CLR 3.0
  • 原文地址:https://www.cnblogs.com/zhaosq/p/10219634.html
Copyright © 2020-2023  润新知