在上一篇文章责任链模式
中提到了模板方法模式,因此这里简单介绍一下.
模板方法模式比较简单,或者说比较常用.在开发过程中,许多人在不知不觉的情况下就会使用,只要他具有良好的面对对象思维.
比如当你写了Dog
和Cat
两个类,发现很多相同的代码,你自然就会将相同模块提取抽象成父类,然后将一些公共的方法放到父类中,这样子就基本实现了模板方式模式.
在一个方法中定义一个算法的骨架,而将一些详细的步骤延迟到子类中.
模板方法使得子类可以在不改变算法结果的基础上,重新定义算法中的某些步骤.
抽象模板: 抽象模板一般有一个具体实现的方法,用来定义算法的基础骨架.还有一些抽象方法留给子类去具体实现.此外还有一些有默认实现的钩子方法.子类可选实现.
具体模板: 继承父类的具体方法,实现他们的抽象方法,对于钩子方法,可以根据自身情况决定是都重写.
书上的例子好多了,网络上也有很多,我自己临时瞎想一个吧,不保证一定合适.
假如我们现在要实现两个类,Dog
和Cat
,并且实现他们的进攻方法.
我们大致实现以下,如下.
Cat:
package design_patterns.template_pattern;
/**
* created by huyanshi on 2019/3/20
*/
public class Cat1 {
private void attack() {
prepared();
jump();
bite();
}
private void prepared() {
}
private void jump() {
}
private void bite() {
}
}
Dog:
package design_patterns.template_pattern;
/**
* created by huyanshi on 2019/3/20
*/
public class Dog1 {
private void attack(){
prepared();
run();
shout();
bite();
}
private void prepared(){
}
private void run(){
}
private void shout(){
}
private void bite(){
}
}
仔细查看代码,发现其实他们的攻击过程很相似.
狗:准备,跑过去,发出声音,咬住
猫:准备,跳过去,咬住
但是这样子编码的话,我们将相同的prepared
和bite
分别写了两次,这是不科学的.
很明显我们可以实现一个父类,将prepared
和bite
在父类里实现他们分别继承就好了.
那么对于shout
和jump/run
方法呢?就分别实现了嘛?
不是的,jump
和run
都是移动,只是实现方法不同,也是可以抽象到父类的,那么shout
呢?这就是钩子的作用了,可以动态控制当前动物是否会发出声音之后再进行攻击
.
改进后的代码如下:
首先是动物模板类:
package design_patterns.template_pattern;
/**
* created by huyanshi on 2019/3/20
*/
public abstract class AnimalTemplate {
protected boolean isShout;
public final void attack() {
//
prepared();
move();
if (isShout) {
shout();
}
bite();
}
private void prepared() {
//具体实现准备方案
System.out.println("准备");
}
abstract void move();
private void bite() {
//具体实现咬的方式
System.out.println("咬");
}
abstract void shout();
public void setShout() {
isShout = true;
}
}
注意,代码中的setShout
是钩子方法,这里简单的用一个变量来当做钩子,此外,prepared
和bite
是具体方法,而move
和shout
为抽象方法.
下面是狗的具体实现:
package design_patterns.template_pattern;
/**
* created by huyanshi on 2019/3/20
*/
public class Dog extends AnimalTemplate {
@Override
void move() {
System.out.println("我是狗,我跑过去");
}
@Override
void shout() {
System.out.println("我是狗,我叫一下,吓唬吓唬他");
}
}
猫的具体实现:
package design_patterns.template_pattern;
/**
* created by huyanshi on 2019/3/20
*/
public class Cat extends AnimalTemplate {
@Override
void move() {
System.out.println("我是猫,我跳过去.");
}
@Override
void shout() {
System.out.println("我是猫,我不叫.");
}
@Override
public void setShout() {
this.isShout = false;
}
}
测试类:
package design_patterns.template_pattern;
/**
* created by huyanshi on 2019/3/20
*/
public class Test {
public static void main(String[] args) {
AnimalTemplate dog = new Dog();
dog.setShout();
dog.attack();
AnimalTemplate cat = new Cat();
cat.setShout();
cat.attack();
}
}
输出结果:
准备
我是狗,我跑过去
我是狗,我叫一下,吓唬吓唬他
咬
准备
我是猫,我跳过去.
咬
对比上下的代码可以发现,在第二个版本中,没有一些重复的代码,且子类的逻辑更加清晰,仅仅实现了自己与其他类不相同的部分,或者自己想要实现的部分钩子方法.
而且当动物越来越多,代码的总量会越来越少且容易维护,新添加一个动物,只需要继承动物模板,然后实现move
和shout
即可.
首先注意一下:在模板方法的attack
上,添加了final
关键字,可以防止该方法被重写,可以保证attack
这一方法,在定义及流程上的正确性及安全性,而具体的实现可以交给子类.
模板方法的优点:
1、封装不变部分,扩展可变部分。
2、提取公共代码,便于维护。
3、行为由父类控制,子类实现。
缺点:
类的个数较多.
完。
以上皆为个人所思所得,如有错误欢迎评论区指正。
欢迎转载,烦请署名并保留原文链接。
联系邮箱:[email protected]
更多学习笔记见个人博客------>呼延十