背景
按理说如果被人写的好,那我还写个啥子?没啥,就是感觉自己写一遍能记得更清楚。
模板的定义,大概就是某件事情,大家大部分步骤是一样的,甚至某些步骤的做法是一模一样的。这时候就有人把这个事情抽象出一个模板来了,方便后面的人按照模板去完成。
定义一个操作中的算法骨架,而将一些实现步骤延迟到子类中。它使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。它的要义就是定义算法骨架,让子类自己实现。
情景
比方新人入职步骤:交必要的入职材料给hr(入职流程审核需要),入职体检,去公司办理入职。
package com.aliyu.learn.pattern.template;
/**
* 描述:入职步骤模板
* <p>作者: aliyu
* <p>创建时间: 2021-10-20 4:26 下午
*/
public abstract class EntryTemplate {
/**
* 入职人的姓名(模板共有属性,在模板初始化时传入)
*/
protected String personName;
public EntryTemplate(String personName) {
this.personName = personName;
}
/**
* 提交材料,具体材料肯定是每个人都不一样
* 交由新人自己实现
* protected保证只能被子类访问
* 非公共的抽成抽象方法,由子类自己实现
*/
protected abstract void uploadData();
/**
* 入职体检,啥时候体检,去哪里体检都不一定一样
* 交由新人自己实现
*/
protected abstract void checkBody();
/**
* 公司报道,具体报道时间不一样
* 交由新人自己实现
*/
protected abstract void checkIn();
/**
* 但是大家都会经历这3个步骤
* 公共的结构化的逻辑在抽象类中实现
* 用final修饰,保证子类不会修改
*/
public final void CheckInStep(){
uploadData();
checkBody();
checkIn();
}
}
ps:公共的步骤逻辑在抽象类中实现(CheckInStep),并用final修饰,不让子类修改。非公共的逻辑抽象为抽象方法,用protected修饰,保证只能由子类自己去实现。
模拟一个张三的模板:
package com.aliyu.learn.pattern.template;
/**
* 描述:张三入职
* <p>作者: aliyu
* <p>创建时间: 2021-10-20 4:49 下午
*/
public class EntryOne extends EntryTemplate{
public EntryOne(String personName) {
super(personName);
}
@Override
protected void uploadData() {
System.out.println("新人张三上传文档");
}
@Override
protected void checkBody() {
System.out.println("张三入职体检");
}
@Override
protected void checkIn() {
System.out.println("张三公司报道");
}
}
李四的模板:
package com.aliyu.learn.pattern.template;
/**
* 描述:李四入职
* <p>作者: aliyu
* <p>创建时间: 2021-10-20 4:49 下午
*/
public class EntryLi extends EntryTemplate{
public EntryLi(String personName) {
super(personName);
}
@Override
protected void uploadData() {
System.out.println("李四上传文档");
}
@Override
protected void checkBody() {
System.out.println("李四入职体检");
}
@Override
protected void checkIn() {
System.out.println("李四公司报道");
}
}
测试张三、李四按照模板进行“入职”:
package com.aliyu.learn.pattern.template;
/**
* 描述:
* <p>作者: aliyu
* <p>创建时间: 2021-10-20 5:00 下午
*/
public class TestTemplate {
public static void main(String[] args) {
EntryLi entryLi = new EntryLi("李四");
entryLi.checkInStep();
EntryZhang entryZhang = new EntryZhang("张三");
entryZhang.checkInStep();
}
}
钩子方法
为啥叫钩子方法?不晓得。它的由来是,比方上面的三个步骤中,如果你有一年内体检报告,那么就没必要体检了。但是这个步骤可能是少数情况,你又不想为了这个重新写一个模板。当然你可以说不体检的人,实现体检方法时,啥也不做就好。这种写法是不优雅的,也容易造成误解。有就是有,没有就是没有。你实现了“体检”方法,却啥也不做?
实现代码:
package com.aliyu.learn.pattern.template;
/**
* 描述:入职步骤模板
* <p>作者: aliyu
* <p>创建时间: 2021-10-20 4:26 下午
*/
public abstract class EntryTemplate {
/**
* 入职人的姓名(模板共有属性,在模板初始化时传入)
*/
protected String personName;
public EntryTemplate(String personName) {
this.personName = personName;
}
/**
* 提交材料,具体材料肯定是每个人都不一样
* 交由新人自己实现
* protected保证只能被子类访问
* 非公共的抽成抽象方法,由子类自己实现
*/
protected abstract void uploadData();
/**
* 入职体检,啥时候体检,去哪里体检都不一定一样
* 交由新人自己实现
*/
protected abstract void checkBody();
/**
* 公司报道,具体报道时间不一样
* 交由新人自己实现
*/
protected abstract void checkIn();
/**
* 钩子方法,默认返回true
* 默认"体检"
* @return
*/
protected boolean isCheckBody(){
return true;
}
/**
* 但是大家都会经历这3个步骤
* 公共的结构化的逻辑在抽象类中实现
*/
public final void checkInStep(){
System.out.println("入职人:" + personName);
uploadData();
//是否执行体检交由"钩子方法"判断,而钩子方法可以被子类重写
if(this.isCheckBody()){
checkBody();
}
checkIn();
}
}
ps:注意到,它加了一个钩子方法“isCheckBody”,默认返回true。
同时骨架方法“checkInStep”中的加了一个判断,将是否体检交由钩子方法判断。而钩子方法可以被子类重写,也就是子类可以自行决定是否参加体检。
不参加体检的王五:
package com.aliyu.learn.pattern.template;
/**
* 描述:王五的体检
* <p>作者: aliyu
* <p>创建时间: 2021-10-21 9:41 上午
*/
public class EntryWang extends EntryTemplate{
private boolean checkBodyFlag = true;
public EntryWang(String personName) {
super(personName);
}
public EntryWang(String personName, boolean checkBodyFlag) {
super(personName);
this.checkBodyFlag = checkBodyFlag;
}
@Override
protected void uploadData() {
System.out.println("王五上传文档");
}
@Override
protected void checkBody() {
System.out.println("王五入职体检");
}
@Override
protected void checkIn() {
System.out.println("王五公司报道");
}
@Override
protected boolean isCheckBody(){
return this.checkBodyFlag;
}
}
ps:加了一个属性“是否体检”和对应的构造方法,使得王五初始化的时候就可以指定自己是否参加体检。
测试代码:
package com.aliyu.learn.pattern.template;
/**
* 描述:
* <p>作者: aliyu
* <p>创建时间: 2021-10-20 5:00 下午
*/
public class TestHookTemplate {
public static void main(String[] args) {
EntryLi entryLi = new EntryLi("李四");
entryLi.checkInStep();
EntryWang entryWang = new EntryWang("王五", false);
entryWang.checkInStep();
}
}
可以看到,王五确实没有体检了。