本文介绍: 解释器模式是一种行为设计模式,它定义了一个语言语法,并用一个解释器来解释该语言中的句子。通常,解释器模式用于将一个复杂的语言拆分成一些简单语言元素,使它们易于理解和操作

一、模板方法模式

1.1概述

定义一个操作算法框架,而将一些步骤延迟到子类中模板方法使得子类可以不改变一个算法结构即可定义算法的某些特定步骤。

例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。 

1.2结构

模板方法(Template Method模式包含以下主要角色:

1.3实现

【例】炒菜

炒菜的步骤是固定的,分为倒油、热油、倒蔬菜、倒调料品、翻炒等步骤。现通过模板方法模式来用代码模拟。类图如下:

 抽象类(Abstract Class)

package com.yanyu.Template;

public abstract class AbstractClass {

    // 模板方法,定义了烹饪的步骤
    public final void cookProcess() {
        //第一步:倒油
        this.pourOil();
        //第二步:热油
        this.heatOil();
        //第三步:倒蔬菜
        this.pourVegetable();
        //第四步:倒调味料
        this.pourSauce();
        //第五步:翻炒
        this.fry();
    }

    public void pourOil() {
        System.out.println("倒油");
    }

    // 抽象方法,由子类实现,倒蔬菜的步骤
    public abstract void pourVegetable();

    // 抽象方法,由子类实现,倒调味料的步骤
    public abstract void pourSauce();

    // 具体方法,热油的步骤是一样的,直接实现
    public void heatOil() {
        System.out.println("热油");
    }

    // 具体方法,翻炒的步骤是一样的,直接实现
    public void fry(){
        System.out.println("炒啊炒啊炒到熟啊");
    }
}

具体子类(Concrete Class)

package com.yanyu.Template;

public class ConcreteClass_BaoCai extends AbstractClass {

    @Override
    public void pourVegetable() {
        System.out.println("下锅的蔬菜是包菜");
    }
    @Override
    public void pourSauce() {
        System.out.println("下锅的酱料是辣椒");
    }
}
package com.yanyu.Template;

public class ConcreteClass_CaiXin extends AbstractClass {
    @Override
    public void pourVegetable() {
        System.out.println("下锅的蔬菜是菜心");
    }

    @Override
    public void pourSauce() {
        System.out.println("下锅的酱料是蒜蓉");
    }
}

客户端

package com.yanyu.Template;

public class Client {
    public static void main(String[] args) {
        //炒手撕包菜
        ConcreteClass_BaoCai baoCai = new ConcreteClass_BaoCai();
        baoCai.cookProcess();

        //炒蒜蓉菜心
        ConcreteClass_CaiXin caiXin = new ConcreteClass_CaiXin();
        caiXin.cookProcess();
    }
}

注意:为防止恶意操作,一般模板方法都加上 final 关键词

1.4优缺点

优点:

缺点:

1.5应用场景

  • 当你只希望客户端扩展某个特定算法步骤,而不是整个算法或其结构时,可使用模板方法模式;

  • 模板方法将整个算法转换为一系列独立的步骤,以便子类能对其进行扩展,同时还可让超类中所定义的结构保持完整;

  • 当多个类的算法除一些细微不同之外几乎完全一样时,你可使用该模式。但其后果就是, 只要算法发生变化,你就可能需要修改所有的类;

  • 在将算法转换为模板方法时,你可将相似的实现步骤提取到超类中以去除重复代码。子类间各不同的代码可继续保留在子类中。

  • 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制

1.6源码解析

InputStream类就使用了模板方法模式。在InputStream类中定义了多个 read() 方法,如下:


public abstract class InputStream implements Closeable {
    //抽象方法,要求子类必须重写
    public abstract int read() throws IOException;
​
    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }
​
    public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }
​
        int c = read(); //调用了无参的read方法,该方法是每次读取一个字节数据
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;
​
        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }
}

从上面代码可以看到,无参的 read() 方法是抽象方法,要求子类必须实现。而 read(byte b[]) 方法调用了 read(byte b[], int off, int len) 方法,所以在此处重点看的方法是带三个参数的方法。

在该方法中第18行、27行,可以看到调用了无参的抽象的 read() 方法。

总结如下: 在InputStream父类中已经定义好了读取一个字节数组数据的方法是每次读取一个字节,并将其存储数组的第一个索引位置读取len字节数据。具体如何读取一个字节数据呢?由子类实现。

二、解释器模式

2.1概述

解释器模式是一种行为型设计模式,它定义了一个语言的语法,并用一个解释器来解释该语言中的句子。通常,解释器模式用于将一个复杂的语言拆分成一些简单的语言元素,使它们易于理解和操作

2.2结构 

  • 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。定义了一个抽象的解释操作,所有具体的表达式都需要实现这个接口
  • 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。它实现了抽象表达式的解释方法。

2.3实现

【例】设计实现加减法的软件

抽象表达式

package com.yanyu.Expressioner;

//抽象角色AbstractExpression
public abstract class AbstractExpression {
    //定义了一个解释器方法,接收一个上下文对象返回解释结果
    public abstract int interpret(Context context);
}

终结符表达式(Terminal Expression)角色

package com.yanyu.Expressioner;

// 终结符表达式角色 变量表达式
// 变量表达式是解释器模式中的一种角色,用于表示语言中的变量。在这里,Variable类表示一个变量,它继承自抽象表达式角色AbstractExpression。
public class Variable extends AbstractExpression {
    private String name;

    // 构造函数用于初始化变量名
    public Variable(String name) {
        this.name = name;
    }

    // interpret方法用于解释上下文中的表达式,这里返回变量对应的值
    @Override
    public int interpret(Context ctx) {
        return ctx.getValue(this);
    }

    // 重写toString方法,返回变量名字符串表示
    @Override
    public String toString() {
        return name;
    }
}
package com.yanyu.Expressioner;

// 终结符表达式角色
// 终结符表达式是解释器模式中的一种角色,用于表示语言中的基本元素。在这里,Value类表示一个具体的值,它继承自抽象表达式角色AbstractExpression。
public class Value extends AbstractExpression {
    private int value;

    // 构造函数,用于初始化值
    public Value(int value) {
        this.value = value;
    }

    // interpret方法用于解释上下文中的表达式,这里是返回值本身
    @Override
    public int interpret(Context context) {
        return value;
    }

    // 重写toString方法,返回值的字符串表示
    @Override
    public String toString() {
        return Integer.valueOf(value).toString();
    }
}

非终结符表达式(Nonterminal Expression)角色

package com.yanyu.Expressioner;

// 非终结符表达式角色  加法表达式
// 加法表达式是解释器模式中的一种非终结符表达式角色,用于表示语言中的加法操作。在这里,Plus类表示加法表达式,它继承自抽象表达式角色AbstractExpression。

public class Plus extends AbstractExpression {
    private AbstractExpression left;  // 左操作数
    private AbstractExpression right;  // 右操作数

    // 构造函数,用于初始化左右操作数
    public Plus(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }

    // interpret方法用于解释上下文中的表达式,这里是返回左右操作数的解释结果相加的值
    @Override
    public int interpret(Context context) {
        return left.interpret(context) + right.interpret(context);
    }

    // 重写toString方法,返回加法表达式的字符串表示,形式为 (左操作数 + 右操作数)
    @Override
    public String toString() {
        return "(" + left.toString() + " + " + right.toString() + ")";
    }
}
package com.yanyu.Expressioner;

///非终结符表达式角色 减法表达式
public class Minus extends AbstractExpression {
    private AbstractExpression left;
    private AbstractExpression right;
    public Minus(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret(Context context) {
        return left.interpret(context) - right.interpret(context);
    }
    @Override
    public String toString() {
        return "(" + left.toString() + " - " + right.toString() + ")";
    }
}

环境(Context)角色

package com.yanyu.Expressioner;

import java.util.HashMap;
import java.util.Map;

// 环境类
// 环境类用于存储变量和它们的值,在解释器模式中起到承上启下的作用,为解释器提供解释所需的上下文信息。
public class Context {
    private Map<Variable, Integer> map = new HashMap<Variable, Integer>();

    // 将变量和对应的值存入map中
    public void assign(Variable var, Integer value) {
        map.put(var, value);
    }

    // 获取变量对应的值
    public int getValue(Variable var) {
        Integer value = map.get(var);
        return value;
    }
}

客户端

package com.yanyu.Expressioner;

// 测试类
// 客户端类Client用于测试解释器模式的功能。在这里我们创建了一个上下文对象context,以及五个变量a、b、c、d、e,并为这些变量赋值。
// 然后我们构造了一个复杂的表达式,包括加法和减法操作,并通过interpret方法解释这个表达式,输出计算结果。

public class Client {
    public static void main(String[] args) {
        Context context = new Context();  // 创建上下文对象

        Variable a = new Variable("a");  // 创建变量a
        Variable b = new Variable("b");  // 创建变量b
        Variable c = new Variable("c");  // 创建变量c
        Variable d = new Variable("d");  // 创建变量d
        Variable e = new Variable("e");  // 创建变量e

        context.assign(a, 1);  // 为变量a赋值
        context.assign(b, 2);  // 为变量b赋值
        context.assign(c, 3);  // 为变量c赋值
        context.assign(d, 4);  // 为变量d赋值
        context.assign(e, 5);  // 为变量e赋值

        // 构造复杂的表达式,包括加法和减法操作
        AbstractExpression expression = new Minus(new Plus(new Plus(new Plus(a, b), c), d), e);

        // 解释并输出表达式的计算结果
        System.out.println(expression + "= " + expression.interpret(context));
    }
}

2.4 优缺点

1,优点:

  • 易于改变和扩展文法。

    由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。

  • 实现文法较为容易。

    在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂。

  • 增加新的解释表达式较为方便。

    如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合 “开闭原则“。

2,缺点:

  • 对于复杂文法难以维护。

    在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护。

2.5应用场景

  • 当语言的文法较为简单,且执行效率不是关键问题时。
  • 问题重复出现,且可以用一种简单的语言来进行表达时。
  • 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候。

三、模板方法模式实验

任务描述

高校网上办事系统中的需要对场地预约设备维修、教职工请假、车辆登记等申请单进行审核,因此需要与众多子系统进行对接子系统会将用户填写好的申请单推送到网上办事系统中,处理完后将单号返回给子系统对接的申请单种类有多个,每种申请单的处理流程是一样的(数据校验-申请单解析-申请单入库-提交审核-自动备份),最大的区别在于不同的申请单,解析方法不同。

本关任务:以场地预约申请单(VenueApplication)和教职工请假申请单(LeaveApplication)为例,模拟实现申请单处理流程

实现方式

  1. 分析目标算法, 确定能否将其分解为多个步骤。 从所有子类的角度出发, 考虑哪些步骤能够通用, 哪些步骤各不相同;

  2. 创建抽象基类并声明一个模板方法和代表算法步骤的一系列抽象方法。 在模板方法中根据算法结构依次调用相应步骤。 可用 final 最终修饰模板方法以防止子类对其进行重写;

  3. 虽然可将所有步骤全都设为抽象类型, 但默认实现可能会给部分步骤带来好处, 因为子类无需实现那些方法;

  4. 可考虑在算法的关键步骤之间添加钩子;

  5. 每个算法变体新建一个具体子类, 它必须实现所有的抽象步骤, 也可以重写部分可选步骤。

编程要求

根据提示,补充右侧编辑器文件Client.javaBegin-End 内的代码,完成实验。其它文件的代码不需要修改。

测试说明

平台会对你编写的代码进行测试

测试输入张三 LeaveApplication 预期输出: 张三数据校验 教职工请假申请单数据解析 张三申请单入库 张三提交审核 张三自动存档

测试输入报告厅 VenueApplication 预期输出: 报告厅数据校验 场地预约申请单数据解析 报告厅申请单入库 报告厅提交审核 报告厅自动存档

抽象类

public abstract class ApplicationTemplate {
    public boolean execute(String data){
        this.checker(data);
        this.dataAnalysis(data);
        this.proposalSave(data);
        this.submit(data);
        this.autoSave(data);
        return true;
    }

    /**
     * 数据校验
     */
    public void checker(String data){
        System.out.println(data+"数据校验");
    }
    /**
     * 数据解析
     */
    public abstract void dataAnalysis(String data);

    /**
     * 数据入库
     */
    public void proposalSave(String data){
        System.out.println(data+"申请单入库");
    }

    /**
     * 提交审核
     */
    public void submit(String data){
        System.out.println(data+"提交审核");
    }

    /**
     * 自动存档
     */
    public void autoSave(String data){
        System.out.println(data+"自动存档");
    }

}

 具体类

public class LeaveApplication extends ApplicationTemplate{
    @Override
    public void dataAnalysis(String data) {
        System.out.println("教职工请假申请单数据解析");
    }
}
public class VenueApplication extends ApplicationTemplate{
    @Override
    public void dataAnalysis(String data) {
        System.out.println("场地预约申请单数据解析");
    }
}

客户端类

import java.util.Scanner;

public class Client {
    public static void main(String[] args) {
        /********** Begin *********/
        Scanner scanner = new Scanner(System.in);
        String applicant = scanner.nextLine();
        String applicationType = scanner.nextLine();

        if (applicationType.equals("LeaveApplication")) {
            LeaveApplication leaveApplication = new LeaveApplication();
            leaveApplication.execute(applicant);
        } else if (applicationType.equals("VenueApplication")) {
            VenueApplication venueApplication = new VenueApplication();
            venueApplication.execute(applicant);
        } else {
            System.out.println("Invalid application type");
        }

        


        /********** End *********/
    }
}

原文地址:https://blog.csdn.net/qq_62377885/article/details/134596871

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_5873.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注