本文节选自《设计模式就该这样学》

通常的模板方法模式中会设计一个abstract的抽象方法,交给它的子类实现,这个方法称为模板方法。而钩子方法,是对于抽象方法或者接口中定义的方法的一个空实现,也是模板方法模式的一种实现方式。

1 模板方法模式中的钩子方法我们以网络课程创建流程为例:发布预习资料 → 制作课件PPT → 在线直播 → 提交课堂笔记 → 提交源码 → 布置作业 → 检查作业。首先创建AbastractCourse抽象类。

代码语言:txt复制public abstract class AbastractCourse {

public final void createCourse(){

//1.发布预习资料

postPreResoucse();

//2.制作课件PPT

createPPT();

//3.在线直播

liveVideo();

//4.上传课后资料

postResource();

//5.布置作业

postHomework();

if(needCheckHomework()){

checkHomework();

}

}

protected abstract void checkHomework();

//钩子方法

protected boolean needCheckHomework(){return false;}

protected void postHomework(){

System.out.println("布置作业");

}

protected void postResource(){

System.out.println("上传课后资料");

}

protected void liveVideo(){

System.out.println("直播授课");

}

protected void createPPT(){

System.out.println("制作课件");

}

protected void postPreResoucse(){

System.out.println("发布预习资料");

}

}上面代码中有个钩子方法,可能有些小伙伴不是太理解,在此笔者稍做解释。设计钩子方法的主要目的是干预执行流程,使得控制行为流程更加灵活,更符合实际业务的需求。钩子方法的返回值一般为适合条件分支语句的返回值(如boolean、int等)。小伙伴们可以根据自己的业务场景决定是否需要使用钩子方法。

然后创建JavaCourse类。

代码语言:txt复制public class JavaCourse extends AbastractCourse {

private boolean needCheckHomework = false;

public void setNeedCheckHomework(boolean needCheckHomework) {

this.needCheckHomework = needCheckHomework;

}

@Override

protected boolean needCheckHomework() {

return this.needCheckHomework;

}

protected void checkHomework() {

System.out.println("检查Java作业");

}

}创建PythonCourse类。

代码语言:txt复制public class PythonCourse extends AbastractCourse {

protected void checkHomework() {

System.out.println("检查Python作业");

}

}最后编写客户端测试代码。

代码语言:txt复制 public static void main(String[] args) {

System.out.println("=========架构师课程=========");

JavaCourse java = new JavaCourse();

java.setNeedCheckHomework(false);

java.createCourse();

System.out.println("=========Python课程=========");

PythonCourse python = new PythonCourse();

python.createCourse();

}

通过这样一个案例,相信小伙伴们对模板方法模式有了一个基本的印象。为了加深理解,我们结合一个常见的业务场景进行介绍。

2 使用模板方法模式重构JDBC业务操作创建一个模板类JdbcTemplate,封装所有的JDBC操作。以查询为例,每次查询的表都不同,返回的数据结构也就都不一样。我们针对不同的数据,都要封装成不同的实体对象。而每个实体封装的逻辑都是不一样的,但封装前和封装后的处理流程是不变的,因此,可以使用模板方法模式设计这样的业务场景。首先创建约束ORM逻辑的接口RowMapper。

代码语言:txt复制/**

* ORM映射定制化的接口

* Created by Tom.

*/

public interface RowMapper {

T mapRow(ResultSet rs,int rowNum) throws Exception;

}然后创建封装了所有处理流程的抽象类JdbcTemplate。

代码语言:txt复制public abstract class JdbcTemplate {

private DataSource dataSource;

public JdbcTemplate(DataSource dataSource) {

this.dataSource = dataSource;

}

public final List executeQuery(String sql,RowMapper rowMapper,Object[] values){

try {

//1.获取连接

Connection conn = this.getConnection();

//2.创建语句集

PreparedStatement pstm = this.createPrepareStatement(conn,sql);

//3.执行语句集

ResultSet rs = this.executeQuery(pstm,values);

//4.处理结果集

List result = this.parseResultSet(rs,rowMapper);

//5.关闭结果集

rs.close();

//6.关闭语句集

pstm.close();

//7.关闭连接

conn.close();

return result;

}catch (Exception e){

e.printStackTrace();

}

return null;

}

private List parseResultSet(ResultSet rs, RowMapper rowMapper) throws Exception {

List result = new ArrayList();

int rowNum = 0;

while (rs.next()){

result.add(rowMapper.mapRow(rs,rowNum++));

}

return result;

}

private ResultSet executeQuery(PreparedStatement pstm, Object[] values) throws SQLException {

for (int i = 0; i < values.length; i++) {

pstm.setObject(i,values[i]);

}

return pstm.executeQuery();

}

private PreparedStatement createPrepareStatement(Connection conn, String sql) throws SQLException {

return conn.prepareStatement(sql);

}

private Connection getConnection() throws SQLException {

return this.dataSource.getConnection();

}

}创建实体对象Member类。

代码语言:txt复制public class Member {

private String username;

private String password;

private String nickname;

private int age;

private String addr;

public String getUsername() {

return username;

}

public void setUsername(String username) {

this.username = username;

}

public String getPassword() {

return password;

}

public void setPassword(String password) {

this.password = password;

}

public String getNickname() {

return nickname;

}

public void setNickname(String nickname) {

this.nickname = nickname;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public String getAddr() {

return addr;

}

public void setAddr(String addr) {

this.addr = addr;

}

}创建数据库操作类MemberDao。

代码语言:txt复制public class MemberDao extends JdbcTemplate {

public MemberDao(DataSource dataSource) {

super(dataSource);

}

public List selectAll(){

String sql = "select * from t_member";

return super.executeQuery(sql, new RowMapper() {

public Member mapRow(ResultSet rs, int rowNum) throws Exception {

Member member = new Member();

//字段过多,原型模式

member.setUsername(rs.getString("username"));

member.setPassword(rs.getString("password"));

member.setAge(rs.getInt("age"));

member.setAddr(rs.getString("addr"));

return member;

}

},null);

}

}最后编写客户端测试代码。

代码语言:txt复制public static void main(String[] args) {

MemberDao memberDao = new MemberDao(null);

List result = memberDao.selectAll();

System.out.println(result);

}希望通过这两个案例的业务场景分析,小伙伴们能够对模板方法模式有更深的理解。

关注微信公众号『 Tom弹架构 』回复“设计模式”可获取完整源码。

【推荐】Tom弹架构:30个设计模式真实案例(附源码),挑战年薪60W不是梦

本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!undefined如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。关注微信公众号『 Tom弹架构 』可获取更多技术干货!