模板方法模式

在讲述这个模式之前,我们先看一个案例:抄题目:两个学生将老师出的题目抄写在纸上,并且写出答案

先看一个比较笨的写法

public class TestPaperA {
    public void testQuestion1(){
        System.out.println("1+1等于几? a.1    b.2     c.3    d.4");
        System.out.println("答案:b");
    }
    
    public void testQuestion2(){
        System.out.println("1*1等于几? a.1    b.2     c.3    d.4");
        System.out.println("答案:a");
    }
    
    public void testQuestion3(){
        System.out.println("1/1等于几? a.1    b.2     c.3    d.4");
        System.out.println("答案:a");
    }
}

public class TestPaperB {
    public void testQuestion1(){
        System.out.println("1+1等于几? a.1    b.2     c.3    d.4");
        System.out.println("答案:c");
    }
    
    public void testQuestion2(){
        System.out.println("1*1等于几? a.1    b.2     c.3    d.4");
        System.out.println("答案:a");
    }
    
    public void testQuestion3(){
        System.out.println("1/1等于几? a.1    b.2     c.3    d.4");
        System.out.println("答案:d");
    }
}

public class Test {
    public static void main(String[] args) {
        System.out.println("学生甲抄的试卷:");
        TestPaperA studentA= new TestPaperA();
        studentA.testQuestion1();
        studentA.testQuestion2();
        studentA.testQuestion3();
        
        System.out.println("学生乙抄的试卷:");
        TestPaperB studentB= new TestPaperB();
        studentB.testQuestion1();
        studentB.testQuestion2();
        studentB.testQuestion3();
    }
}

输出结果:

学生甲抄的试卷:
1+1等于几? a.1 b.2 c.3 d.4
答案:b
1*1等于几? a.1 b.2 c.3 d.4
答案:a
1/1等于几? a.1 b.2 c.3 d.4
答案:a
学生乙抄的试卷:
1+1等于几? a.1 b.2 c.3 d.4
答案:c
1*1等于几? a.1 b.2 c.3 d.4
答案:a
1/1等于几? a.1 b.2 c.3 d.4
答案:d

可以看出,学生甲和学生乙除了答案不一样,抄的题目都一样,抄题目的过程容易出错,而且如果老师改了题目,那么两个学生都需要把题目改掉

怎么优化?我们先来一个初步优化:学过继承的都会想到,把公共部分放到父类中,子类继承父类后,自然就拥有了公共部分

父类
public class TestPaper {
    public void testQuestion1(){
        System.out.println("1+1等于几? a.1    b.2     c.3    d.4");
    }
    
    public void testQuestion2(){
        System.out.println("1*1等于几? a.1    b.2     c.3    d.4");
    }
    
    public void testQuestion3(){
        System.out.println("1/1等于几? a.1    b.2     c.3    d.4");
    }
}

子类
public class TestPaperA extends TestPaper{
    @Override
    public void testQuestion1(){
        super.testQuestion1();
        System.out.println("答案:b");
    }
    @Override
    public void testQuestion2(){
        super.testQuestion2();
        System.out.println("答案:a");
    }
    @Override
    public void testQuestion3(){
        super.testQuestion3();
        System.out.println("答案:a");
    }
}

public class TestPaperB extends TestPaper{
    @Override
    public void testQuestion1(){
        super.testQuestion1();
        System.out.println("答案:c");
    }
    @Override
    public void testQuestion2(){
        super.testQuestion2();
        System.out.println("答案:a");
    }
    @Override
    public void testQuestion3(){
        super.testQuestion3();
        System.out.println("答案:d");
    }
}

测试类同上

我们看这个初步优化,发现还是有重复的部分,比如super.testQuestion1()和System.out.println("答案”)

我们既然用了继承,并且肯定这个继承有意义,就应该要成为子类的模板,所有重复的代码都应该要上升到父类去,而不是让每个子类都去重复。

对于“抄题目”这个例子来说,除了学生的答案会有不同的结果,其他全部都是一样的。继续优化:

修改父类
public abstract class TestPaper {
    public void testQuestion1(){
        System.out.println("1+1等于几? a.1    b.2     c.3    d.4");
        System.out.println("答案:"+answer1());
    }
    
    public void testQuestion2(){
        System.out.println("1*1等于几? a.1    b.2     c.3    d.4");
        System.out.println("答案:"+answer2());
    }
    
    public void testQuestion3(){
        System.out.println("1/1等于几? a.1    b.2     c.3    d.4");
        System.out.println("答案:"+answer3());
    }
    
    public abstract String answer1();
    
    public abstract String answer2();
    
    public abstract String answer3();
}

修改子类
public class TestPaperA extends TestPaper{
    @Override
    public String answer1() {
        return "b";
    }
    @Override
    public String answer2() {
        return "a";
    }
    @Override
    public String answer3() {
        return "a";
    }
}

public class TestPaperB extends TestPaper{
    @Override
    public String answer1() {
        return "c";
    }
    @Override
    public String answer2() {
        return "a";
    }
    @Override
    public String answer3() {
        return "d";
    }
}

测试类
public class Test {
    public static void main(String[] args) {
        System.out.println("学生甲抄的试卷:");
        TestPaper studentA= new TestPaperA();
        studentA.testQuestion1();
        studentA.testQuestion2();
        studentA.testQuestion3();
        
        System.out.println("学生乙抄的试卷:");
        TestPaper studentB= new TestPaperB();
        studentB.testQuestion1();
        studentB.testQuestion2();
        studentB.testQuestion3();
    }
}

输出结果:

学生甲抄的试卷:
1+1等于几? a.1 b.2 c.3 d.4
答案:b
1*1等于几? a.1 b.2 c.3 d.4
答案:a
1/1等于几? a.1 b.2 c.3 d.4
答案:a
学生乙抄的试卷:
1+1等于几? a.1 b.2 c.3 d.4
答案:c
1*1等于几? a.1 b.2 c.3 d.4
答案:a
1/1等于几? a.1 b.2 c.3 d.4
答案:d

结果和之前一模一样,但简洁了很多。此时要有更多的学生来答卷,只不过是在试卷的木板上填写选择题的选项答案,这是每个人的试卷唯一不同(谁说的,名字也不同,但这样的做法的确是对试卷的最大复用)

下面介绍模板方法模式:http://www.runoob.com/design-pattern/template-pattern.html

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

AbstractClass是抽象类,其实也就是一抽象模板,定义并实现了一个模板方法。
这个模板方法一般是一个具体方法。它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的
抽象操作中,推迟到子类实现。

public abstract class AbstractClass {
    //一些抽象行为,放到子类去实现
    public abstract void primitiveOperation1();
    public abstract void primitiveOperation2();
    
    //模板方法,给出了逻辑的骨架,而逻辑的组成是一些相应的抽象操作,它们都推迟到子类去实现
    public void templateMethod(){
        primitiveOperation1();
        primitiveOperation2();
    }
}

ConcreteClass实现父类所定义的一个或多个抽象方法。每一个AbstractClass都可以有任意多个ConcreteClass与之对应,而每一个ConcreteClass都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。

public class ConcreteClassA extends AbstractClass{
    @Override
    public void primitiveOperation1() {
        //具体类A方法1实现,与ConcreteClassB不同的方法实现
    }

    @Override
    public void primitiveOperation2() {
        //具体类A方法2实现,与ConcreteClassB不同的方法实现
    }
}

public class ConcreteClassB extends AbstractClass{
    @Override
    public void primitiveOperation1() {
        //具体类B方法1实现,与ConcreteClassA不同的方法实现
    }

    @Override
    public void primitiveOperation2() {
        //具体类B方法2实现,与ConcreteClassA不同的方法实现
    }
}

测试代码

public class Test {
    public static void main(String[] args) {
        AbstractClass c = null;
        
        c = new ConcreteClassA();
        c.templateMethod();
        
        c = new ConcreteClassB();
        c.templateMethod();
    }
}

模板方法模式是通过把不变行为搬移到超类,去除子类中的重复代码来体现它的优势。

模板方法模式就是提供了一个很好的代码复用平台。因为有时候,我们会遇到由一系列步骤构成的过程需要执行。这个过程从高层次上看是相同的,但有些步骤的实现可能不同。这时候,我们通常就应该要考虑用模板方法模式了。

当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。我们通过模板方法模式把这些行为搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠

原文地址:https://www.cnblogs.com/jwen1994/p/10011927.html