模板方法模式定义
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况,重新定义算法中的某些步骤。
类图:
AbstractClass:这个抽象类包含了模板方法。模板方法执行了一系列的算法(方法)。模板方法本身和抽象方法的具体实现之间被解耦了。
ConcreteClass:这个具体类实现了抽象的操作,当模板方法需要这两个抽象方法时,会调用实现它们的子类。
模板方法是用来创建一个算法的模板。模板就是一个方法。更具体地说,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现。这样可以确保算法的结构保持不变,同时由子类提供部分实现。
模板方法定义了一连串的步骤,每个步骤由一个方法代表。
我们还可以有“默认不做事的方法”,我们称这种方法为“hook”(钩子)。子类可以视情况决定要不要覆盖它们。
“hook”(钩子)是一种被声明在抽象类中的方法。但它只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩(这是事先将钩子放在了某个点上)。要不要挂钩,由子类自行决定。
如果要使用钩子,就必须在子类中覆盖它。
对于抽象父类中的方法,我们这样考虑:当我们的子类“必须”提供算法中某个方法或步骤的实现时,就声明为抽象方法;如果算法的这个部分是可选的,就声明为钩子(一个空的或有默认实现的方法)。如果是钩子的话,子类可以选择实现这个钩子,但并不强制这么做。当某些步骤是可选的,我们可以将这些步骤设计成钩子,而不是抽象方法。这样可以让抽象类的子类的负荷减轻。
我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些低层组件。换句话说,高层组件对待低层组件的方式是“别调用我们,我们会调用你”。低层组件挂载上系统的点,是高层模块事先决定的。当我们设计模板方法时,告诉子类“不要调用我们,我们会调用你“。
模板方法模式是一个很常见的模式,因为对创建框架来说,由框架控制如何做事情,而由你(使用这个框架的人)指定框架算法中每个步骤的细节。
使用钩子的实例
import javax.swing.*;
import java.awt.*;
public class MyFrame extends JFrame { public MyFrame(String title){ super(title); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(300,300); this.setVisible(true); } public void paint(Graphics graphics){ super.paint(graphics); String msg = "I rule!"; graphics.drawString(msg,100,100); }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
上面这个实例,我们扩展了JFrame,它包含一个update()方法,这个方法控制更新屏幕的算法。我们可以通过覆盖paint()钩子方法和这个算法挂上钩。
JFrame的更新算法被称为paint()。在默认状态下,paint()方法是不做事的…它只是一个钩子。我们覆盖paint(),是为了让JFrame在容器上面画出一条消息。
测试:
public class Main { public static void main(String[] args) { MyFrame myFrame = new MyFrame("Hello world"); }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
测试结果会弹出一个窗口。
还有大量的applet使用钩子来提供行为。因为这些行为是作为钩子实现的,所以Applet类就不用去实现它们。
我们再来看看一个例子,再次说明模板方法模式是怎么用的。我们以煮茶与煮咖啡为例,它们两者都涉及煮水、倒水(倒茶或倒咖啡)。不同的是冲泡的过程和添加的配料不同,茶要泡,配料可加糖或柠檬或不加,咖啡要冲再过滤,可以加牛奶或糖或不加:
1.共同的部分写在父类AbstractClassCaffeineBeverageWithHook中,不同部分(异变部分)用抽象方法表示,由子类来实现,父类要提供一个模板方法封装冲茶或冲咖啡的每一个过程(算法步骤),如下面这个父类中的prepareRecipe方法:
package abstract_class;
public abstract class AbstractClassCaffeineBeverageWithHook { final public void prepareRecipe(){ boilWater(); brew(); pourInCup(); if(customerWantsCondiments()){ addCondiments(); } } protected abstract void brew(); protected abstract void addCondiments(); public void boilWater(){ System.out.println("Boiling water"); } public void pourInCup(){ System.out.println("Pouring water"); } //在这里定义了一个方法,通常是空的缺省实现。这个方法只会返回true,不做别的事 //这是一个钩子,子类可以覆盖这个方法,但不见得一定要这么做 protected boolean customerWantsCondiments(){ return true; }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
2.子类
CoffeeWithHook
package concrete_class;
import abstract_class.AbstractClassCaffeineBeverageWithHook;
public class CoffeeWithHook extends AbstractClassCaffeineBeverageWithHook { @Override protected void brew() { System.out.println("Dripping Coffee through filter"); } @Override protected void addCondiments() { System.out.println("Add sugar and Milk"); } @Override protected boolean customerWantsCondiments() { //覆盖这个钩子,提供自己的功能,不返回true了,返回false。即不加任何东西 return false; }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
TeaWithNoHook,它不重要钩子,即默认要加配料:
package concrete_class;
import abstract_class.AbstractClassCaffeineBeverageWithHook;
public class TeaWithNoHook extends AbstractClassCaffeineBeverageWithHook { @Override protected void brew() { System.out.println("steeping the tea"); } @Override protected void addCondiments() { System.out.println("Add lemon"); }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
3.测试
import abstract_class.AbstractClassCaffeineBeverageWithHook;
import concrete_class.CoffeeWithHook;
import concrete_class.TeaWithNoHook;
public class Main { public static void main(String[] args) {
// MyFrame myFrame = new MyFrame("Hello world"); AbstractClassCaffeineBeverageWithHook coffee = new CoffeeWithHook(); coffee.prepareRecipe(); AbstractClassCaffeineBeverageWithHook tea = new TeaWithNoHook(); tea.prepareRecipe(); }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
4.测试结果
Boiling water
Dripping Coffee through filter
Pouring water
Boiling water
steeping the tea
Pouring water
Add lemon
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
小结:
- “模板方法”定义了算法的步骤,把这些步骤的实现延迟到子类。
- 模板方法模式为我们提供了一种代码复用的重要技巧。
- 模板方法的抽象类可以定义具体方法、抽象方法和钩子。
- 抽象方法由子类实现。
- 钩子是一种方法,它在抽象类中不做事,或者只做默认的事情,子类可以选择要不要去覆盖它。
- 为了防止子类改变模板方法中的算法,可以将模板方法声明为final。
- 将决策权放在高层模块中,以便决定如何以及何时调用低层模块。
- 策略模式和模板方法模式都封装算法,一个用组合,一个用继承。
- 工厂方法是模板方法的一种特殊版本。
demo示例代码
谢谢阅读
文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/weixin_40763897/article/details/88944435
评论 (0)
登录后可评论,请 登录 或 注册
评论您没有权限执行当前操作