今天给大家介绍的是:模版方法模式,这个模式非常的常见,很多开发者,无意中其实就已经用到了,也非常简单,只是还不知道,这属于一种设计模式而已,而关于这个设计模式,在网上有很多专业性词语来说明,看的人审美疲劳。
其实这个设计模式,通俗易懂的来讲,就是把子类重复的东西,定义到父类中模块化起来,父类只需要去管理好算法流程框架,把经常变化的点,让子类去继承完成。
我们看下面几道案例来理解就OK了:
案例一:
- 在很久很久以前,有一个小山村,住在这里面的小朋友上学无比困难,因为这个小村庄太过于偏僻,像是被大山围住了一样,出行非常不便,
- 于是这个小山村里面的村民,就集体花钱给这个小村庄办了一个小学校,经过两年后,也正是的开始在上课了,晓明是这个学校的学生,学习非常
- 努力,学习也是这个班级比较厉害的,有一次班级考试,由于学校资金紧缺,没有试卷,所以学校老师把题目写在黑板上,让学生们抄下来,作为
- 试卷题目,由于之前晓明看书离书比较近,眼睛成了近视眼,把黑板上的题目抄错了,把里面的数字7抄写成了1,晓明自信满满的完成了考试,
- 也认为没有问题,而老师在批改的时候不会很认真的看题目,只是去对照答案…,最后等试卷发下来才知道原因,回到家中还被父母批评,
- 晓明很苦恼…….
我们先把晓明的这次题目抄错:用代码实现…..
晓明:
/**
* 用此类模拟晓明
* @author Liudeli
*/
public class XiaoMing {
/**
* 题目一
* @param answer 传入参数为答案
*/
public void subject1(String answer) {
System.out.println("1)在Java中,如果父类中的某些方法不包含任何逻辑,并且需要有子类重写,应该使用()关键字来申明父类的这些方法。"
+ "a) Final "
+ "b) Static "
+ "c) Abstract "
+ "d) Void"
+ "");
System.out.println("答案是:" + answer);
}
/**
* 题目二
* @param answer 传入参数为答案
*/
public void subject2(String answer) {
System.out.println("2) 在java中,以定义了两个接口B和C,要定义一个实现这两个接口的类,以下语句正确的是()"
+ "a) interface A extends B,C "
+ "b) interface A implements B,C "
+ "c) class A implements B,C"
+ "d) class A implements B,implements C "
+ "");
System.out.println("答案是:" + answer);
}
/**
* 题目三
* @param answer 传入参数为答案
*/
public void subject3(String answer) {
System.out.println("3) 在java中,在定义类时加上修饰符()可以实现该类不能在本类被实例化。"
+ "a) Final "
+ "b) Public "
+ "c) c) Private "
+ "d) Abstract "
+ "");
System.out.println("答案是:" + answer);
}
}
晓明同学A:
/**
* 同学A
* @author Liudeli
*/
public class StudentA {
/**
* 题目一
* @param answer 传入参数为答案
*/
public void subject1(String answer) {
System.out.println("1)在Java中,如果父类中的某些方法不包含任何逻辑,并且需要有子类重写,应该使用()关键字来申明父类的这些方法。"
+ "a) Final "
+ "b) Static "
+ "c) Abstract "
+ "d) Void"
+ "");
System.out.println("答案是:" + answer);
}
/**
* 题目二
* @param answer 传入参数为答案
*/
public void subject2(String answer) {
System.out.println("2) 在java中,以定义了两个接口B和C,要定义一个实现这两个接口的类,以下语句正确的是()"
+ "a) interface A extends B,C "
+ "b) interface A implements B,C "
+ "c) class A implements B,C"
+ "d) class A implements B,implements C "
+ "");
System.out.println("答案是:" + answer);
}
/**
* 题目三
* @param answer 传入参数为答案
*/
public void subject3(String answer) {
System.out.println("3) 在java中,在定义类时加上修饰符()可以实现该类不能在本类被实例化。"
+ "a) Final "
+ "b) Public "
+ "c) c) Private "
+ "d) Abstract "
+ "");
System.out.println("答案是:" + answer);
}
}
晓明同学B:
/**
* 同学B
* @author Liudeli
*/
public class StudentB {
/**
* 题目一
* @param answer 传入参数为答案
*/
public void subject1(String answer) {
System.out.println("1)在Java中,如果父类中的某些方法不包含任何逻辑,并且需要有子类重写,应该使用()关键字来申明父类的这些方法。"
+ "a) Final "
+ "b) Static "
+ "c) Abstract "
+ "d) Void"
+ "");
System.out.println("答案是:" + answer);
}
/**
* 题目二
* @param answer 传入参数为答案
*/
public void subject2(String answer) {
System.out.println("2) 在java中,以定义了两个接口B和C,要定义一个实现这两个接口的类,以下语句正确的是()"
+ "a) interface A extends B,C "
+ "b) interface A implements B,C "
+ "c) class A implements B,C"
+ "d) class A implements B,implements C "
+ "");
System.out.println("答案是:" + answer);
}
/**
* 题目三
* @param answer 传入参数为答案
*/
public void subject3(String answer) {
System.out.println("3) 在java中,在定义类时加上修饰符()可以实现该类不能在本类被实例化。"
+ "a) Final "
+ "b) Public "
+ "c) Private "
+ "d) Abstract "
+ "");
System.out.println("答案是:" + answer);
}
}
考试的很多同学回答(举例三个,其他省略….)
/**
* 测试程序
* @author Liudeli
*/
public class MainClass {
public static void main(String[] args) {
// 晓明抄了三个题目 而且在考试的那天回答完成...
XiaoMing xiaoMing = new XiaoMing();
xiaoMing.subject1("a");
xiaoMing.subject2("d");
xiaoMing.subject3("b");
// 晓明的同学A抄了三个题目 而且在考试的那天回答完成...
StudentA studentA= new StudentA();
studentA.subject1("c");
studentA.subject2("b");
studentA.subject3("c");
// 晓明抄了三个题目 而且在考试的那天回答完成...
StudentB studentB = new StudentB();
studentB.subject1("c");
studentB.subject2("a");
studentB.subject3("d");
}
}
运行结果:
- 我们现在跳出晓明在小山村上学的故事…,直接回到编程中来分析:
以上的这种写法有很多的问题,晓明/晓明同学A/晓明同学B
省略 …..
抄黑板上面的题目是一模一样的,那么多同学一旦有人把题目抄错了,会造成很多麻烦,而且要抄那么多次,
做了很多重复的事情,而且扩展性不好,维护性也不好,所以我们应该把,这么多同学抄写相同题目给抽取出来
定义父类方法模版:
/**
* 定义一个题目类,把所有的题目给抽取出来,公共出来
* @author Liudeli
*/
public class Subject {
/**
* 题目一
* @param answer 传入参数为答案
*/
public void subject1(String answer) {
System.out.println("1)在Java中,如果父类中的某些方法不包含任何逻辑,并且需要有子类重写,应该使用()关键字来申明父类的这些方法。"
+ "a) Final "
+ "b) Static "
+ "c) Abstract "
+ "d) Void"
+ "");
System.out.println("答案是:" + answer);
}
/**
* 题目二
* @param answer 传入参数为答案
*/
public void subject2(String answer) {
System.out.println("2) 在java中,以定义了两个接口B和C,要定义一个实现这两个接口的类,以下语句正确的是()"
+ "a) interface A extends B,C "
+ "b) interface A implements B,C "
+ "c) class A implements B,C"
+ "d) class A implements B,implements C "
+ "");
System.out.println("答案是:" + answer);
}
/**
* 题目三
* @param answer 传入参数为答案
*/
public void subject3(String answer) {
System.out.println("3) 在java中,在定义类时加上修饰符()可以实现该类不能在本类被实例化。"
+ "a) Final "
+ "b) Public "
+ "c) Private "
+ "d) Abstract "
+ "");
System.out.println("答案是:" + answer);
}
}
晓明:
/**
* 用此类模拟晓明
* @author Liudeli
*/
public class XiaoMing extends Subject{
/**
* 题目一
* @param answer 传入参数为答案
*/
public void subject1(String answer) {
subject1(answer);
}
/**
* 题目二
* @param answer 传入参数为答案
*/
public void subject2(String answer) {
subject2(answer);
}
/**
* 题目三
* @param answer 传入参数为答案
*/
public void subject3(String answer) {
subject3(answer);
}
}
晓明同学A:
/**
* 同学A
* @author Liudeli
*/
public class StudentA extends Subject{
/**
* 题目一
* @param answer 传入参数为答案
*/
public void subject1(String answer) {
subject1(answer);
}
/**
* 题目二
* @param answer 传入参数为答案
*/
public void subject2(String answer) {
subject2(answer);
}
/**
* 题目三
* @param answer 传入参数为答案
*/
public void subject3(String answer) {
subject3(answer);
}
}
晓明同学B:
/**
* 同学B
* @author Liudeli
*/
public class StudentB extends Subject{
/**
* 题目一
* @param answer 传入参数为答案
*/
public void subject1(String answer) {
subject1(answer);
}
/**
* 题目二
* @param answer 传入参数为答案
*/
public void subject2(String answer) {
subject2(answer);
}
/**
* 题目三
* @param answer 传入参数为答案
*/
public void subject3(String answer) {
subject3(answer);
}
}
测试程序:
/**
* 测试程序
* @author Liudeli
*/
public class Main {
public static void main(String[] args) {
Subject xiaoMing = new XiaoMing();
xiaoMing.subject1("c");
xiaoMing.subject2("c");
xiaoMing.subject3("d");
Subject studentA = new StudentA();
studentA.subject1("a");
studentA.subject2("b");
studentA.subject3("c");
Subject studentB = new StudentB();
studentB.subject1("c");
studentB.subject2("b");
studentB.subject3("c");
}
}
运行结果:
从晓明同学类系列中分析,他们还有很多重复的区域,例如:方法重复,所以我们需要一个父类模版
把唯一在变的在子类中完成,把重复的全部都在父类中完成**
查看重复项(晓明类,同学A,同学B…..,他们方法(subject1(), subject2(), subject3())都一模一样,唯一不一样的就是答案,继承的体现要继承的够彻底, 所以要抽取代码):
父类抽取唯一改变点给子类去完成:
/**
* 定义一个题目类,把所有的题目给抽取出来,公共出来
* @author Liudeli
*/
public abstract class Subject {
/**
* 题目一
* @param answer 传入参数为答案
*/
public void subject1() {
System.out.println("1)在Java中,如果父类中的某些方法不包含任何逻辑,并且需要有子类重写,应该使用()关键字来申明父类的这些方法。"
+ "a) Final "
+ "b) Static "
+ "c) Abstract "
+ "d) Void"
+ "");
System.out.println("答案是:" + answerMethod1()); // 注意:这里只需子类去完成,只需学习回答答案即可
}
/**
* 题目二
* @param answer 传入参数为答案
*/
public void subject2() {
System.out.println("2) 在java中,以定义了两个接口B和C,要定义一个实现这两个接口的类,以下语句正确的是()"
+ "a) interface A extends B,C "
+ "b) interface A implements B,C "
+ "c) class A implements B,C"
+ "d) class A implements B,implements C "
+ "");
System.out.println("答案是:" + answerMethod2()); // 注意:这里只需子类去完成,只需学习回答答案即可
}
/**
* 题目三
* @param answer 传入参数为答案
*/
public void subject3() {
System.out.println("3) 在java中,在定义类时加上修饰符()可以实现该类不能在本类被实例化。"
+ "a) Final "
+ "b) Public "
+ "c) Private "
+ "d) Abstract "
+ "");
System.out.println("答案是:" + answerMethod3()); // 注意:这里只需子类去完成,只需学习回答答案即可
}
/**
* 定义答案抽象方法,第一个题目的答案
* @return 返回答案
*/
public abstract String answerMethod1();
/**
* 定义答案抽象方法,第二个题目的答案
* @return 返回答案
*/
public abstract String answerMethod2();
/**
* 定义答案抽象方法,第三个题目的答案
* @return 返回答案
*/
public abstract String answerMethod3();
}
晓明类:
/**
* 用此类模拟晓明
* @author Liudeli
*/
public class XiaoMing extends Subject{
/*
晓明的目的就是填答案,只负责自己的职责,这也是单一职责原则的体现,
其他的试卷制作过程,题目怎么制造出来,根本不需要去关心
*/
public String answerMethod1() {return "a";}
public String answerMethod2() {return "c";}
public String answerMethod3() {return "b";}
}
晓明同学A类:
/**
* 同学A
* @author Liudeli
*/
public class StudentA extends Subject{
/*
晓明的同学A的目的就是填答案,只负责自己的职责,这也是单一职责原则的体现,
其他的试卷制作过程,题目怎么制造出来,根本不需要去关心
*/
public String answerMethod1() {return "c";}
public String answerMethod2() {return "c";}
public String answerMethod3() {return "a";}
}
晓明同学B类:
/**
* 同学B
* @author Liudeli
*/
public class StudentB extends Subject{
/*
晓明的同学A的目的就是填答案,只负责自己的职责,这也是单一职责原则的体现,
其他的试卷制作过程,题目怎么制造出来,根本不需要去关心
*/
public String answerMethod1() {return "c";}
public String answerMethod2() {return "c";}
public String answerMethod3() {return "a";}
}
案例二:
狗,鸡,猫 他们都有 叫的行为,可以这样来实现:
/**
* 定义一个猫类
* @author Liudeli
*/
public class Cat {
/**
* 猫的叫方法
*/
public void call() {
System.out.println("喵喵喵!!!");
}
}
/**
* 定义一个鸡类
* @author Liudeli
*/
public class Chicken {
/**
* 鸡的叫方法
*/
public void call() {
System.out.println("咯咯咯!!!");
}
}
/**
* 定义一个狗类
* @author Liudeli
*/
public class Dog {
/**
* 狗的叫方法
*/
public void call() {
System.out.println("汪汪汪!!!");
}
}
/**
* 测试程序
* @author Liudeli
*/
public class Main {
public static void main(String[] args) {
// 狗,鸡,猫 他们都有 叫的行为,可以这样来实现
Dog dog = new Dog();
dog.call();
Cat cat = new Cat();
cat.call();
Chicken chicken = new Chicken();
chicken.call();
}
}
运行结果:
父类抽取相同项,模版方法方式实现:
狗,鸡,猫 他们都有 叫的行为,而他们的行为相同,都是叫,所以可以把相同的行为抽取出来定义父类模版,
* 这就是方法模版模式了,把唯一不同需要变化的交给子类去做..
/**
* 定义一个动物类,抽取共用的行为,定义成模版
* @author Liudeli
*/
public abstract class Animal {
/**
* 动物的叫方法
*/
public void call() {
System.out.println(callValue());
}
/**
* 叫声是动物唯一不同的,所以这个是具体动物需要变换的值
* 定义抽象 叫声
* @return 返回叫声
*/
public abstract String callValue();
}
/**
* 定义一个猫类
* @author Liudeli
*/
public class Cat extends Animal{
/**
* 猫的叫声
*/
public String callValue() {
return "喵喵喵!!!";
}
}
/**
* 定义一个鸡类
* @author Liudeli
*/
public class Chicken extends Animal{
/**
* 鸡的叫声
*/
public String callValue() {
return "咯咯咯!!!";
}
}
/**
* 定义一个狗类
* @author Liudeli
*/
public class Dog extends Animal{
/**
* 狗的叫声
*/
public String callValue() {
return "汪汪汪!!!";
}
}
/**
* 测试程序
* @author Liudeli
*/
public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
dog.call();
Animal cat = new Cat();
cat.call();
Animal chicken = new Chicken();
chicken.call();
}
}
运行结果:
案例三:
我们来简单模拟 Android Activity 执行过程,一个应用App有很多的Activity,登录,注册,主界面
省略…
这些界面中,都有返回操作,隐藏标题栏操作,等等,都把这些共同点抽取到父类模块
错误写法:
/**
* 简单的模拟Android中的Activity
* @author Liudeli
*/
public class Activity {
/**
* 模拟Android中的 onCreate()方法
*/
public void onCreate() {
}
/**
* 模拟Android中的 onDestroy()方法
*/
public void onDestroy() {
}
}
/**
* 模拟Activity中的登录操作
* @author Liudeli
*/
public class LoginActivity extends Activity {
/**
* 模拟登录中的 onCreate()方法
*/
public void onCreate() {
System.out.println("onCreate()...");
}
/**
* 模拟登录中的 onDestroy()方法
*/
public void onDestroy() {
System.out.println("onDestory()...");
}
/**
* 模拟返回
*/
public void back() {
System.out.println("登录界面返回操作...");
}
/**
* 隐藏标题栏
*/
public void hideTitle() {
System.out.println("登录界面隐藏标题栏操作...");
}
}
/**
* 模拟Activity中的主界面操作
* @author Liudeli
*/
public class MainActivity extends Activity {
/**
* 模拟主界面中的 onCreate()方法
*/
public void onCreate() {
System.out.println("onCreate()...");
}
/**
* 模拟主界面中的 onDestroy()方法
*/
public void onDestroy() {
System.out.println("onDestory()...");
}
/**
* 模拟返回
*/
public void back() {
System.out.println("主界面返回操作...");
}
/**
* 隐藏标题栏
*/
public void hideTitle() {
System.out.println("主界面隐藏标题栏操作...");
}
}
/**
* 模拟Activity中的注册操作
* @author Liudeli
*/
public class RegisterActivity extends Activity {
/**
* 模拟注册中的 onCreate()方法
*/
public void onCreate() {
System.out.println("onCreate()...");
}
/**
* 模拟注册中的 onDestroy()方法
*/
public void onDestroy() {
System.out.println("onDestory()...");
}
/**
* 模拟返回
*/
public void back() {
System.out.println("注册界面返回操作...");
}
/**
* 隐藏标题栏
*/
public void hideTitle() {
System.out.println("注册界面隐藏标题栏操作...");
}
}
提炼代码:
/**
* 简单的模拟Android中的Activity
* @author Liudeli
*/
public class Activity {
/**
* 模拟Android中的 onCreate()方法
*/
public void onCreate() {
}
/**
* 模拟Android中的 onDestroy()方法
*/
public void onDestroy() {
}
}
/**
* 抽取共用的行为
* @author Liudeli
*/
public class BaseActivity extends Activity{
/**
* 模拟返回
*/
public void back() {
System.out.println("界面返回操作...");
}
/**
* 隐藏标题栏
*/
public void hideTitle() {
System.out.println("界面隐藏标题栏操作...");
}
}
/**
* 模拟Activity中的登录操作
* @author Liudeli
*/
public class LoginActivity extends BaseActivity {
/**
* 模拟登录中的 onCreate()方法
*/
public void onCreate() {
// 在某种业务情况下,隐藏标题栏
if (true) {
hideTitle();
}
System.out.println("onCreate()...");
}
/**
* 模拟登录中的 onDestroy()方法
*/
public void onDestroy() {
// 在某种业务情况下,隐藏标题栏
if (true) {
back();
}
System.out.println("onDestory()...");
}
}
/**
* 模拟Activity中的主界面操作
* @author Liudeli
*/
public class MainActivity extends BaseActivity {
/**
* 模拟主界面中的 onCreate()方法
*/
public void onCreate() {
// 在某种业务情况下,隐藏标题栏
if (true) {
hideTitle();
}
System.out.println("onCreate()...");
}
/**
* 模拟主界面中的 onDestroy()方法
*/
public void onDestroy() {
// 在某种业务情况下,隐藏标题栏
if (true) {
back();
}
System.out.println("onDestory()...");
}
}
/**
* 模拟Activity中的注册操作
* @author Liudeli
*/
public class RegisterActivity extends BaseActivity {
/**
* 模拟注册中的 onCreate()方法
*/
public void onCreate() {
// 在某种业务情况下,隐藏标题栏
if (true) {
back();
}
System.out.println("onCreate()...");
}
/**
* 模拟注册中的 onDestroy()方法
*/
public void onDestroy() {
// 在某种业务情况下,隐藏标题栏
if (true) {
back();
}
System.out.println("onDestory()...");
}
}
案例四:
模拟:
小军在一家小型创业公司,公司开发Java的人员,就他一个人,他在实现一个功能,此功能
就是传入两个数,在传入运算符进行计算,把结果用漂亮的界面显示出来!
最近公司新招聘了一个Java工程师(小欧),不如把工作任务分担,把一些计算功能给小欧来完成,把界面显示就小军自己来完成
/**
* 定义一个运算类
* @author Liudeli
*/
public abstract class Operation {
/**
* 一个简单的方法,计算两个值,把结果用漂亮界面显示出来
* @param number1
* @param type
* @param number2
*/
public void showResultStyle(double number1, String type, double number2) {
// 把这个计算的功能,给小欧去做
double result = operationResult(number1, type, number2);
// 小军值关注html 样式界面显示出来就可以了
System.out.println("<html 这里面写了漂亮的样式....>结果是:" + result + "</html>");
}
/**
* 定义一个抽象函数,此函数为计算方法,把这个计算方法给小欧去完成
* @param number1
* @param type
* @param number2
* @return
*/
public abstract double operationResult(double number1, String type, double number2);
}
把运算功能给新来的同事小欧去做,小欧只需继承父类方法,完成子类要完成的功能即可:
/**
* 这个类是用于计算数值
* @author Liudeli
*/
public class Algorithm extends Operation{
public double operationResult(double number1, String type, double number2) {
double resultOperator = 0;
try {
if ("+".equals(type)) {
resultOperator = number1 + number2;
} else if ("-".equals(type)) {
resultOperator = number1 - number2;
} else if ("*".equals(type)) {
resultOperator = number1 * number2;
} else if ("/".equals(type)) {
resultOperator = number1 / number2;
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("输入有误!!! e:" + e.toString());
}
return resultOperator;
}
}
子类与子类多个子类都要相同的行为,应该把这些行为,用父类方法模版抽取出来,子类只完成变化点即可,这也属于开放封闭式原则,更好的维护性,扩展性,同时自然也是复用性!
谢谢大家的观看,更多精彩技术博客,会不断的更新,请大家访问,
刘德利CSDN博客, http://blog.csdn.net/u011967006