手写JdbcTemplate的模板方法模式
模板方法模式
模板方法模式(Template Method Pattern)又叫模板模式,是指定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤,属于行为型设计模式。
模板方法模式实际上是封装了一个固定流程,该流程由几个步骤组成,具体步骤可以由子类进行不同的实现,从而让固定的流程产生不同的结果。模板方法的本质就是抽象封装流程,然后进行类的继承实现。
通用UML类图
举例说明
我们平时在家自己做饭, (1)买菜,(2)洗菜, (3)做菜,(4)吃饭,(5)有时间的话洗碗,没时间的话就放到明天再洗,这五步就是定义的算法的流程;然后做不同菜需要买不同的材料,洗不同的材料,做法也不一样,这个就需要子类去具体实现。然后对于父类已经定义好的算法我们想做一些微调,那就是通过重写和钩子方法。
先定义一个做饭的算法,定义一个抽象类DodishTemplate,吃饭和洗碗没啥区别,已经实现好了,买菜,洗菜,做菜根据不同的菜做法是不一样的,需要子类自己去实现。第五步洗碗 haveTime 这个就是钩子方法,到底有没有时间由子类自己去考虑。
public abstract class DodishTemplate {
/**
* 做饭的过程
*/
public final void dodish() {
//1、买菜
preparation();
//2、洗菜
washVegetables();
//3、做菜
doing();
//4、吃饭
eatting();
//5、洗碗
if (haveTime()) {
washDishes();
}
}
protected abstract void preparation();
protected abstract void washVegetables();
protected abstract void doing();
protected void eatting() {
System.out.println("吃饭");
}
//钩子方法
protected abstract boolean haveTime();
protected void washDishes() {
System.out.println("洗碗");
}
}
实现一个西红柿炒蛋类EggsWithTomato,买菜洗菜做菜,有没有时间子类都已经实现了,最后一个吃饭很喜欢吃,所以重写了父类的吃饭。
/**
* 西红柿炒蛋
*/
public class EggsWithTomato extends DodishTemplate {
@Override
protected void preparation() {
System.out.println("买西红柿和鸡蛋");
}
@Override
protected void washVegetables() {
System.out.println("洗西红柿和鸡蛋");
}
@Override
protected void doing() {
System.out.println("做西红柿炒蛋");
}
@Override
protected boolean haveTime() {
return false;
}
//重写
@Override
protected void eatting() {
System.out.println("吃的很开心");
}
}
实现一个红烧肉类RedBraisedMeat,买菜洗菜做菜,有没有时间子类都已经实现了
public class RedBraisedMeat extends DodishTemplate {
@Override
protected void preparation() {
System.out.println("买肉");
}
@Override
protected void washVegetables() {
System.out.println("洗肉");
}
@Override
protected void doing() {
System.out.println("做红烧肉");
}
@Override
protected boolean haveTime() {
return true;
}
}
最后写个测试类测试一下
public class Test {
public static void main(String[] args) {
System.out.println("=========做西红柿鸡蛋流程=========");
EggsWithTomato eggsWithTomato = new EggsWithTomato();
eggsWithTomato.dodish();
System.out.println("=========做红烧肉流程=========");
RedBraisedMeat redBraisedMeat = new RedBraisedMeat();
redBraisedMeat.dodish();
}
}
这个就是结果,在模板方法模式中,包含了抽象方法、具体方法和钩子方法,所以通过实现,重写,钩子可以达到差异化。
手写JDBC
创建一个模板类JdbcTemplate,封装所有的JDBC操作。以查询为例,每次查询的表不同,返回的数据结构也就不一样。我们针对不同的数据结构,都要封装成不同的实体对象,而每个实体封装的逻辑都是不一样的,但封装前和封装后的处理流程是不变的。因此,我们可以使用模板方法模式来设计这样的业务场景。
创建封装了所以处理流程的抽象类JdbcTemplate:
public abstract class JdbcTemplate {
private DataSource dataSource;
public JdbcTemplate(DataSource dataSource) {
this.dataSource = dataSource;
}
public final List<?> executeQuery(String sql, Object[] values) {
try {
//1、获取连接
Connection conn = this.getConnection();
//2、创建语句集
PreparedStatement pre = this.createPrepareStatement(conn, sql);
//3、执行语句集
ResultSet rs = this.executeQuery(pre, values);
//4、处理结果集
List<Object> result = new ArrayList<>();
while (rs.next()) {
result.add(rowMapper(rs));
}
//5、关闭结果集
rs.close();
//6、关闭语句集
pre.close();
//7、关闭连接
conn.close();
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//实现
protected abstract Object rowMapper(ResultSet rs) throws SQLException;
//这个是不让重写的
private ResultSet executeQuery(PreparedStatement pre, Object[] values) throws SQLException {
for (int i = 0; i < values.length; i++) {
pre.setObject(i, values[i]);
}
return pre.executeQuery();
}
//这个是不让重写的
private PreparedStatement createPrepareStatement(Connection conn, String sql) throws SQLException {
return conn.prepareStatement(sql);
}
//这个是不让重写的
private Connection getConnection() throws SQLException {
return this.dataSource.getConnection();
}
}
创建实体对象Member类:
@Data
public class Member {
private String username;
private String password;
private String nickname;
private int age;
private String addr;
}
创建数据库操作类MemberDao:
public class MemberDao extends JdbcTemplate {
public MemberDao(DataSource dataSource) {
super(dataSource);
}
public List<?> selectAll() {
String sql = "select * from t_member";
return super.executeQuery(sql, null);
}
@Override
protected Object rowMapper(ResultSet rs) throws SQLException {
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;
}
}
客户端测试代码:
public class Test {
public static void main(String[] args) {
MemberDao memberDao = new MemberDao(null);
List<?> result = memberDao.selectAll();
System.out.println(result);
}
}
优缺点
优点:
- 利用模板方法将相同处理逻辑的代码放到抽象父类中,可以提高代码的复用性。
- 将不同的代码不同的子类中,通过对子类的扩展增加新的行为,提高代码的扩展性。
- 把不变的行为写在父类上,去除子类的重复代码,提供了一个很好的代码复用平台,符合开闭原则。
缺点:
- 类数目的增加,每一个抽象类都需要一个子类来实现,这样导致类的个数增加。
- 类数量的增加,间接地增加了系统的复杂度。
- 继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍。
本文及其他设计模式源码:github.com/xuhaoj/patt…
后记
在《论语·为政》中孔子说“吾十有五而志于学,三十而立,四十而不惑,五十而知天命,六十而耳顺,七十而从心所欲,不逾矩”。小伙们知道这句话运用的是哪个设计模式吗,这个其实就是本文讲的模板模式,我们可以把这句话理解为一套骨架算法,这个是改变不了的,我们每个人能做的就是实现这个接口或者抽象类,往自己的10-20岁,20-30岁,30-40岁。。。方法中间填充内容,而填充的丰富多彩,多姿多彩,还是平庸无奇,浑浑噩噩,全靠我们自己的想法。
小伙伴们,人的一生很短,十年也是一晃而过,我也是2010年上大学,到2020年四年大学,六年社会,仿佛就在昨天,2010年上海举办世博会,那时候我也到上海来参观,那时候浦东还是很破的,而现在浦东发展的今非昔比,十年足矣改变一个人。我也曾经爱玩过,我也曾经迷茫过,而现在我的眼里只是多了一份坚定,活到老学到老。现在我有能力了,希望通过写博客能够帮助到迷茫的人,种一棵树最好的时间是十年前,其次是现在,只要你行动了,任何时候都不晚。谢谢大家的观看~
转载自:https://juejin.cn/post/7004832193298530312