Java设计模式(十四)—— 模板方法模式
模板方法模式是指定义一个操作中算法的骨架,而将一些步骤延迟到子类中。模板方法使子类可以不改变一个算法的结构,即可重定义该算法的某些特定步骤。
适合模板方法模式的情景如下:
- 编制一个通用算法,将某些步骤的具体实现留给子类来实现
- 需要重构代码,将各个子类的公共行为提取到一个共同的父类中,避免代码重复
一、方法模板
1. 自定义方法模板
(1)下面是编制求对象数组最大值的泛型类
public interface ILess<T> {
boolean less(T X, T y);
}
泛型方法类:
public class Algo<T> {
public T getMax(T t[], ILess<T> cmp) {
T maxValue = t[0];
//遍历数组,找出最大值赋值给maxValue
for (int i = 1; i < t.length; i++) {
if (cmp.less(maxValue, t[i])) {
maxValue = t[i];
}
}
return maxValue;
}
}
(3)ILess子类的实现
public class InteLess implements ILess<Integer>{
@Override
public boolean less(Integer x, Integer y) {
return x < y;
}
}
(4)学生基本类
public class Student {
String name;
int grade;//成绩
public Student(String name, int grade) {
this.name = name;
this.grade = grade;
}
}
(5)学生成绩比较器
public class StudLess implements ILess<Student>{
@Override
public boolean less(Student x, Student y) {
return x.grade < y.grade;
}
}
(6)测试类
public class Test {
public static void main(String[] args) {
//初始化一个泛型方法类
Algo<Integer> obj = new Algo<>();
//初始化一个ILess的子类对象 int类型的比较器
ILess<Integer> cmp = new InteLess();
Integer a[] = {3,5,2,7};
//传入一个比较数组,传入一个比较器
Integer max = obj.getMax(a,cmp);
System.out.println("最大值是:"+max);
Algo<Student> obj2 = new Algo<>();
ILess<Student> cmp2 = new StudLess();
Student s[] = {new Student("zy",89),new Student("smm",96),new Student("minmin",84)};
Student max2 = obj2.getMax(s, cmp2);
System.out.println(max2.name+"的成绩最高,是:"+max2.grade);
}
}
输出:
最大值是:7
smm的成绩最高,是:96
从上面可以看出,若实现求某类对象数组的最大值,主要工作是编制具体的从ILess接口派生的子类代码,重写less() 方法,自定义比较规则即可。
二、流程模板
流程模板就是指做一些任务的通用流程,它是指写一个操作中的算法框架,而将一些步骤延迟到子类中去实现,这样就使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
流程模板模式各个角色具体描述如下所示:
- AbstractCalss:抽象类。用来定义算法骨架。具体的子类通过重写这些操作来实现一个算法的各个步骤。
- ConcreteClass:具体类。用来实现算法骨架中的某些步骤,完成与特定子类向相关的功能。
考虑这样一个示例:
求圆和长方形的面积,要求输入不同形状的参数:圆需要输入半径r,长方形需要输入长、宽。
根据题意,求任意形状的面积流程包含两部分:输入参数过程、计算面积过程。据此编制的功能类如下所示:
(1)AbsShape
public abstract class AbsShape {
public double process() {
input();
double value = getArea();
return value;
}
protected abstract void input();
public abstract double getArea();
}
(2)两个具体的形状功能类
这两个类根据自身特点重写了input() 和 getArea() 方法。
public class Circle extends AbsShape{
//半径
double r;
@Override
public void input() {
System.out.println("请输入半径:");
Scanner scanner = new Scanner(System.in);
r = scanner.nextDouble();
}
@Override
public double getArea() {
double s = Math.PI*r*r;
return s;
}
}
public class Rect extends AbsShape{
double width,height;
@Override
public void input() {
System.out.println("请输入宽和高:");
Scanner s = new Scanner(System.in);
width = s.nextDouble();
height = s.nextDouble();
}
@Override
public double getArea() {
return width * height;
}
}
(3)测试类
public class Test2 {
public static void main(String[] args) {
AbsShape obj = new Circle();
System.out.println(obj.process());
AbsShape obj2 = new Rect();
System.out.println(obj2.process());
}
}
输出:
请输入半径:
4
50.26548245743669
请输入宽和高:
10
5
50.0