柳州关键词优化网站,长沙武广新城建设网站,最牛网站建设是谁,wordpress教程下载地址一、模板方法模式
1.1概述 定义一个操作中算法的框架#xff0c;而将一些步骤延迟到子类中#xff0c;模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 例如#xff0c;去银行办理业务一般要经过以下4个流程#xff1a;取号、排队、办理具体业…一、模板方法模式
1.1概述 定义一个操作中算法的框架而将一些步骤延迟到子类中模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 例如去银行办理业务一般要经过以下4个流程取号、排队、办理具体业务、对银行工作人员进行评分等其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的可以在父类中实现但是办理具体业务却因人而异它可能是存款、取款或者转账等可以延迟到子类中实现。 1.2结构 模板方法Template Method模式包含以下主要角色 抽象类Abstract Class负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成可以是抽象方法和具体方法。 模板方法定义了算法的骨架按某种顺序调用其包含的基本方法。 基本方法是实现算法各个步骤的方法是模板方法的组成部分。基本方法又可以分为三种 抽象方法(Abstract Method) 一个抽象方法由抽象类声明、由其具体子类实现。 具体方法(Concrete Method) 一个具体方法由一个抽象类或具体类声明并实现其子类可以进行覆盖也可以直接继承。 钩子方法(Hook Method) 在抽象类中已经实现包括用于判断的逻辑方法和需要子类重写的空方法两种。抽象类中的可选步骤子类可以选择是否实现。 一般钩子方法是用于判断的逻辑方法这类方法名一般为isXxx返回值类型为boolean类型。 具体子类Concrete Class实现抽象类中所定义的抽象方法和钩子方法它们是一个顶级逻辑的组成步骤。 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 {Overridepublic void pourVegetable() {System.out.println(下锅的蔬菜是包菜);}Overridepublic void pourSauce() {System.out.println(下锅的酱料是辣椒);}
}
package com.yanyu.Template;public class ConcreteClass_CaiXin extends AbstractClass {Overridepublic void pourVegetable() {System.out.println(下锅的蔬菜是菜心);}Overridepublic 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角色是抽象表达式的子类用来实现文法中与终结符相关的操作文法中的每一个终结符都有一个具体终结表达式与之相对应。它实现了抽象表达式的解释方法。 非终结符表达式Nonterminal Expression角色也是抽象表达式的子类用来实现文法中与非终结符相关的操作文法中的每条规则都对应于一个非终结符表达式。它们通过递归的方式来解释语言。 环境Context角色通常包含各个解释器需要的数据或是公共的功能一般用来传递被所有解释器共享的数据后面的解释器可以从这里获取这些值。环境保存了要解释的语言它提供了一个接口给表达式来获取和设置环境的状态。 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方法用于解释上下文中的表达式这里是返回变量对应的值Overridepublic int interpret(Context ctx) {return ctx.getValue(this);}// 重写toString方法返回变量名的字符串表示Overridepublic 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方法用于解释上下文中的表达式这里是返回值本身Overridepublic int interpret(Context context) {return value;}// 重写toString方法返回值的字符串表示Overridepublic 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方法用于解释上下文中的表达式这里是返回左右操作数的解释结果相加的值Overridepublic int interpret(Context context) {return left.interpret(context) right.interpret(context);}// 重写toString方法返回加法表达式的字符串表示形式为 (左操作数 右操作数)Overridepublic 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;}Overridepublic int interpret(Context context) {return left.interpret(context) - right.interpret(context);}Overridepublic String toString() {return ( left.toString() - right.toString() );}
}环境Context角色
package com.yanyu.Expressioner;import java.util.HashMap;
import java.util.Map;// 环境类
// 环境类用于存储变量和它们的值在解释器模式中起到承上启下的作用为解释器提供解释所需的上下文信息。
public class Context {private MapVariable, Integer map new HashMapVariable, 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); // 创建变量aVariable b new Variable(b); // 创建变量bVariable c new Variable(c); // 创建变量cVariable d new Variable(d); // 创建变量dVariable e new Variable(e); // 创建变量econtext.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为例模拟实现申请单处理流程。 实现方式 分析目标算法 确定能否将其分解为多个步骤。 从所有子类的角度出发 考虑哪些步骤能够通用 哪些步骤各不相同 创建抽象基类并声明一个模板方法和代表算法步骤的一系列抽象方法。 在模板方法中根据算法结构依次调用相应步骤。 可用 final 最终修饰模板方法以防止子类对其进行重写 虽然可将所有步骤全都设为抽象类型 但默认实现可能会给部分步骤带来好处 因为子类无需实现那些方法 可考虑在算法的关键步骤之间添加钩子 为每个算法变体新建一个具体子类 它必须实现所有的抽象步骤 也可以重写部分可选步骤。 编程要求 根据提示补充右侧编辑器文件Client.java中 Begin-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{Overridepublic void dataAnalysis(String data) {System.out.println(教职工请假申请单数据解析);}
}public class VenueApplication extends ApplicationTemplate{Overridepublic 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 *********/}
}