设计模式之模板模式

概念

​ 模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。

​ 当项目中出现流程长,但由于在不同场景下具体的实现不同,那么就可以定义算法骨架,即不同场景下共有的逻辑,在需要不同实现的位置抽象为方法,由子类去实现。

模板设计模式解决问题的是,复用 与 扩展。

好处:代码复用,减少重复代码。除了子类要实现的特定方法,其他方法及方法调用顺序都在父类中预先写好了。

使用方式

关键点:

  • 定义算法骨架,抽象为父类;
  • 子类继承父类,由子类实现不同场景下的逻辑;

举个例子

以考试卷子为例,一份试卷需要复印多份给学生去考试,卷子的试题都是一样的,而每一份试卷的简答和名字都不相同。那么就可以抽象出这样的父类。

abstract class Paper {
    abstract String getName();  //获取学生姓名
    abstract String getResult();  //获取学生的答案
    
    public void getPaperContent() {   //获取试卷的试题内容
        System.out.println("我是"+ getName() + ",我的答案是"+ getResult());
        //....
    }
}

那么学生的试卷就可以继承这个父类,具体实现就交给子类去是实现了。

class StudentPaPer1 extends Paper {
    @Override
    public String getName() {
        return "张三";
    }
    @Override
    public String getResult(){
         return "a";
    }
    
}

class StudentPaPer2 extends Paper {
    @Override
    public String getName() {
        return "李四";
    }
    @Override
    public String getResult(){
         return "b";
    }
    
}

那么调用方式就变为


public static void main(String[] args) {
    Paper student1 = new StudentPaPer1();
    Paper student2 = new StudentPaPer2();
    
    student1.getPaperContent();
    student2.getPaperContent();
    //...
}

PS:其实就是利用继承的特性将共有逻辑封装起来;

扩展

​ 使用模板设计模式可以减少重复代码,通过定义抽象方法,由子类实现。使用**回调(Callback)**同样可以起到复用与扩展的作用。

回调的概念

相对于普通的函数调用来说,回调是一种双向调用关系。A 类事先注册某个函数 F 到 B 类,A 类在调用 B 类的 P 函数的时候,B 类反过来调用 A 类注册给它的 F 函数。这里的 F 函数就是“回调函数”。A 调用 B,B 反过来又调用 A,这种调用机制就叫作“回调”。

A类如何将回调函数注册给B类呢?在Java中可以将A类的回调对象传入到B类中。

public interface ICallback {
	void methodToCallback();
}

/**
* 封装公有逻辑
**/
public class B {
	public void process(ICallback callback) {
		//...
		callback.methodToCallback();
		//...
	}
}

// 回调实现类
public class A implements ICallback {
    @Override      
	public void methodToCallback() {     
        System.out.println("call back invoke...");     
    }
}

// 客户端调用
public class Demo {
	public static void main(String[] args) {
		B b = new B();
        // 使用匿名创建对象
		b.prpcess(new ICallback(){
			 @Override      
			 public void methodToCallback() {     
             	System.out.println("call back invoke...");     
             }
		});
        
        // 也可以直接创建A对象实例
        A a = new A();
        b.process(a);
	}
}

在Demo客户端中,创建了B类对象,在调用B#process方法时,创建匿名对象当作是回调对象传给process方法,这样B在执行process中可执行特定的场景方法;

后续有新增的场景,那么只需要创建实现 ICallback 回调类的对象即能实现代码复用及扩展。

场景一:JdbcTemplate

public class JdbcTemplateDemo {
  private JdbcTemplate jdbcTemplate;

  public User queryUser(long id) {
    String sql = "select * from user where id="+id;
    return jdbcTemplate.query(sql, new UserRowMapper()).get(0);
  }

  class UserRowMapper implements RowMapper<User> {
    public User mapRow(ResultSet rs, int rowNum) throws SQLException {
      User user = new User();
      user.setId(rs.getLong("id"));
      user.setName(rs.getString("name"));
      user.setTelephone(rs.getString("telephone"));
      return user;
    }
  }
}

​ JdbcTemplate 里面复用了加载驱动、创建数据库连接、创建 statement、关闭连接、关闭 statement、处理异常等繁琐的流程步骤;针对不同的SQL,其流程代码都是可以复用的;在使用的时候只需要创建RowMapper的实现,将对象当作是回调对象传给方法,从而实现代码复用与扩展。

场景二:JVM的钩子方法

JVM 提供了 Runtime.addShutdownHook(Thread hook) 方法,可以注册一个 JVM 关闭的 Hook。Hook即是钩子,在应用程序关闭的时候,JVM会自动调用Hook代码。

public class ShutdownHookDemo {

  private static class ShutdownHook extends Thread {
    public void run() {
      System.out.println("I am called during shutting down.");
    }
  }

  public static void main(String[] args) {
    Runtime.getRuntime().addShutdownHook(new ShutdownHook());
  }
}

​ 有关 Hook 的逻辑都被封装到 ApplicationShutdownHooks 类中了。当应用程序关闭的时候,JVM 会调用这个类的 runHooks() 方法,创建多个线程,并发地执行多个 Hook。我们在注册完 Hook 之后,并不需要等待 Hook 执行完成,所以,这也算是一种异步回调。

模板设计与回调方法的区别与联系

​ 从代码实现上,回调利用组合关系实现,把一个对象传递给另一个对象,是一种对象之间的关系;模板利用继承关系实现,子类重写父类的抽象方法,是一种类之间的关系;

​ 可以看出,回调相对于模板模式会更加灵活,主要体现在以下几点:

  • Java是单继承的,基于模板模式编写的子类,不再具有继承的特点;
  • 回调可以使用匿名类来创建回调对象,可以不用事先定义类;而模板模式针对不同的实现都要定义不同的子类;
  • 如果某个类中定义了多个模板方法,每个方法都有对应的抽象方法,那即便我们只用到其中的一个模板方法,子类也必须实现所有的抽象方法。而回调就更加灵活,我们只需要往用到的模板方法中注入回调对象即可。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值