今天给大家介绍的是:模版方法模式,这个模式非常的常见,很多开发者,无意中其实就已经用到了,也非常简单,只是还不知道,这属于一种设计模式而已,而关于这个设计模式,在网上有很多专业性词语来说明,看的人审美疲劳。


其实这个设计模式,通俗易懂的来讲,就是把子类重复的东西,定义到父类中模块化起来,父类只需要去管理好算法流程框架,把经常变化的点,让子类去继承完成。


我们看下面几道案例来理解就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