模板模式 向右看齐 2021-09-18 07:26 312阅读 0赞 # 模板模式 # ## 定义 ## 模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。 翻译一下 模板方法模式,是在一个事先定义后的一套业务代码中,将某些可扩展的业务代码推迟到子类中去实现,即事先约定后一套流程,然后对外暴露一些可动态改变的业务。 ## 用途 ## 模板模式作用场景主要有两个: 复用和扩展 适用于一个业务逻辑整体逻辑确定,但是有些业务的具体逻辑需要推迟交给子类去完善。 ## 代码体现 ## 编写公共的流程代码,但是在流程代码中调用抽象方法,不具体实现,推迟给子类实现。 在代码的实现思路上可以更清楚地理解其的使用场景: **1.复用指的的是流程代码的复用性** **2.扩展指的是流程代码中在处理某些业务时候,调用抽象方法,不具体实现,推迟给子类实现。** ## 代码实现 ## public class TemplateMethodPattern { public static void main(String[] args) { AbstractClass tm = new ConcreteClass(); tm.TemplateMethod(); } } //抽象类 abstract class AbstractClass { //模板方法 客户端代码实际调用的方法 public void TemplateMethod() { SpecificMethod(); abstractMethod1(); abstractMethod2(); } //具体方法 某一个业务的公共流程代码 public void SpecificMethod() { System.out.println("抽象类中的具体方法被调用..."); } //抽象方法1 public abstract void abstractMethod1(); //抽象方法2 public abstract void abstractMethod2(); } //具体子类 class ConcreteClass extends AbstractClass { public void abstractMethod1() { System.out.println("抽象方法1的实现被调用..."); } public void abstractMethod2() { System.out.println("抽象方法2的实现被调用..."); } } ## 使用场景 ## ### 1.复用 ### 流程代码的复用性 这里的复用指的是所有的子类都可以复用父类中模板方法定义的流程代码。 例子 Java IO 类库中,有很多类的设计用到了模板模式,比如 InputStream、OutputStream、Reader、Writer。我们拿 InputStream 来举例说明一下。我把 InputStream 部分相关代码贴在了下面。在代码中,read() 函数是一个模板方法,定义了读取数据的整个流程,并且暴露了一个可以由子类来定制的抽象方法。不过这个方法也被命名为了 read(),只是参数跟模板方法不同。 public abstract class InputStream implements Closeable { //...省略其他代码... public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int c = read(); if (c == -1) { return -1; } b[off] = (byte)c; int i = 1; try { for (; i < len ; i++) { //注意这里 调用抽象方法,具体实现推迟给子类 c = read(); if (c == -1) { break; } b[off + i] = (byte)c; } } catch (IOException ee) { } return i; } public abstract int read() throws IOException; } public class ByteArrayInputStream extends InputStream { //...省略其他代码... @Override public synchronized int read() { return (pos < count) ? (buf[pos++] & 0xff) : -1; } } ## 2.扩展 ## 框架的扩展性,其实还是框架流程代码某些具体实现推迟给子类实现。 这里所说的扩展,并不是指代码的扩展性,而是指框架的扩展性。 仍然拿上面的例子举例 上面我们着眼于流程代码的复用性,来说明其适用于可复用的业务场景,同理,其read()方法推迟给子类实现,可以直接体现其适用于扩展的业务场景。 public abstract class InputStream implements Closeable { //...省略其他代码... //这个膜版代码从模板模式的角度看可分为两部分,第一部分体现其流程代码可复用的特点:6-12行和15-32行 第二部分体现其可扩展的特点:14行 调用抽象方法。 public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int c = read(); if (c == -1) { return -1; } b[off] = (byte)c; int i = 1; try { for (; i < len ; i++) { //注意这里 调用抽象方法,具体实现推迟给子类 c = read(); if (c == -1) { break; } b[off + i] = (byte)c; } } catch (IOException ee) { } return i; } public abstract int read() throws IOException; } public class ByteArrayInputStream extends InputStream { //...省略其他代码... @Override public synchronized int read() { return (pos < count) ? (buf[pos++] & 0xff) : -1; } } ## 模板模式的缺点 ## 假设一个框架中的某个类暴露了两个模板方法,并且定义了一堆供模板方法调用的抽象方法,代码示例如下所示。在项目开发中,即便我们只用到这个类的其中一个模板方法,我们还是要在子类中把所有的抽象方法都实现一遍,这相当于无效劳动 public abstract class AbstractClass { public final void templateMethod1() { //... method1(); //... method2(); //... } public final void templateMethod2() { //... method3(); //... method4(); //... } protected abstract void method1(); protected abstract void method2(); protected abstract void method3(); protected abstract void method4(); } 而且模板模式需要子类继承父类,这种模式,让子类放弃了继承其他类的能力,且也有可能需要在子类中实现一些子类根本用不到的抽象类。 模板模式这种思路无法就是先制定一套流程代码,将其中某些实现逻辑推迟给子类去实现。所以按照这个思路出发,代码完全可以有另一套实现形式: 回调 # 回调 # 相对于普通的函数调用来说,回调是一种双向调用关系。A 类事先注册某个函数 F 到 B 类,A 类在调用 B 类的 P 函数的时候,B 类反过来调用 A 类注册给它的 F 函数。这里的 F 函数就是“回调函数”。A 调用 B,B 反过来又调用 A,这种调用机制就叫作“回调”。 即被调用方可以反向调用其调用方的代码。 其实这里说的更多的是同步回调,即被调用者的代码,可以在客户端中实现 ## 代码体现: ## 定义一个回调接口,调用者中某个方法参数为回调接口,客户端使用的时候,必须在其方法中实现回调接口的具体逻辑。 ## code ## 回调接口 1 public interface Callback { 2 String callBack(); 3 } 调用者 public class Another { private Callback callback; //调用实现类的方法 public void setCallback(Callback callback) { this.callback = callback; } //业务需要的时候,通过委派,来调用实现类的具体方法 public void doCallback(){ System.out.println(callback.callBack()); } } 测试回调函数 public class TestCallcack { public static void main(String[] args) { //创建调用者的实现类 Another another = new Another(); //将回掉接口注册到实现类中 another.setCallback(new Callback() { @Override public String callBack() { return "you are a pig"; } }); //执行回调函数 another.doCallback(); } } ## 举例 ## JdbcTemplate 通过回调的机制,将不变的执行流程抽离出来,放到模板方法 execute() 中,将可变的部分设计成回调 StatementCallback,由用户来定制。query() 函数是对 execute() 函数的二次封装,让接口用起来更加方便。 @Override public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException { return query(sql, new RowMapperResultSetExtractor<T>(rowMapper)); } @Override public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException { Assert.notNull(sql, "SQL must not be null"); Assert.notNull(rse, "ResultSetExtractor must not be null"); if (logger.isDebugEnabled()) { logger.debug("Executing SQL query [" + sql + "]"); } //可扩展的地方 设计成回调 实现StatementCallback接口 class QueryStatementCallback implements StatementCallback<T>, SqlProvider { @Override public T doInStatement(Statement stmt) throws SQLException { ResultSet rs = null; try { rs = stmt.executeQuery(sql); ResultSet rsToUse = rs; if (nativeJdbcExtractor != null) { rsToUse = nativeJdbcExtractor.getNativeResultSet(rs); } return rse.extractData(rsToUse); } finally { JdbcUtils.closeResultSet(rs); } } @Override public String getSql() { return sql; } } return execute(new QueryStatementCallback()); } //流程代码 @Override public <T> T execute(StatementCallback<T> action) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); Connection con = DataSourceUtils.getConnection(getDataSource()); Statement stmt = null; try { Connection conToUse = con; if (this.nativeJdbcExtractor != null && this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) { conToUse = this.nativeJdbcExtractor.getNativeConnection(con); } stmt = conToUse.createStatement(); applyStatementSettings(stmt); Statement stmtToUse = stmt; if (this.nativeJdbcExtractor != null) { stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt); } T result = action.doInStatement(stmtToUse); handleWarnings(stmt); return result; } catch (SQLException ex) { // Release Connection early, to avoid potential connection pool deadlock // in the case when the exception translator hasn't been initialized yet. JdbcUtils.closeStatement(stmt); stmt = null; DataSourceUtils.releaseConnection(con, getDataSource()); con = null; throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex); } finally { JdbcUtils.closeStatement(stmt); DataSourceUtils.releaseConnection(con, getDataSource()); } } ## 回调相对于模板模式会更加灵活,主要体现在下面几点 ## * 像 Java 这种只支持单继承的语言,基于模板模式编写的子类,已经继承了一个父类,不再具有继承的能力。 * 回调可以使用匿名类来创建回调对象,可以不用事先定义类;而模板模式针对不同的实现都要定义不同的子类。 * 如果某个类中定义了多个模板方法,每个方法都有对应的抽象方法,那即便我们只用到其中的一个模板方法,子类也必须实现所有的抽象方法。而回调就更加灵活,我们只需要往用到的模板方法中注入回调对象即可。 **综上可以看出** 回调比模板模式代码实现的更加灵活且避免了实现一些多余的方法
相关 模板模式 模板模式 > 模板模式准备一个抽象类,将部分逻辑以具体方法以及具体构造形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法, 一时失言乱红尘/ 2022年11月27日 06:46/ 0 赞/ 75 阅读
相关 模板模式 在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计 偏执的太偏执、/ 2022年11月10日 10:44/ 0 赞/ 122 阅读
相关 模板模式 模板模式 定义: 模板模式是行为型模式的一种。它的定义是一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进 小鱼儿/ 2022年07月13日 12:54/ 0 赞/ 120 阅读
相关 模板模式 介绍 对于某一个业务逻辑(算法实现)在不同的对象中有不同的细节实现,但是逻辑(算法) 的框架(或通用的应用算法)是相同的。`Template`提供了这种情况的一个实现框架 ╰+攻爆jí腚メ/ 2022年05月22日 04:11/ 0 赞/ 118 阅读
相关 模板模式 前言 模板方法模式(TemplateMethod):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某 蔚落/ 2021年12月09日 00:45/ 0 赞/ 197 阅读
相关 模板模式 模板模式 在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行 谁践踏了优雅/ 2021年09月29日 16:06/ 0 赞/ 219 阅读
相关 模板模式 本质:子类重写父类方法,并且使用父类引用指向子类对象,调用此方法是子类的方法。 -------------------- 模板抽象类,定义一些模板方法 publ 浅浅的花香味﹌/ 2021年09月23日 13:48/ 0 赞/ 230 阅读
相关 模板模式 模板模式 定义 模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤 向右看齐/ 2021年09月18日 07:26/ 0 赞/ 313 阅读
相关 模板模式 模板模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,模板模式可以使得子类可以不改变一个算法的结构就可以重新定义改算法的某些特定的步骤。 当我们要完成在某一细节层次 你的名字/ 2021年09月17日 01:10/ 0 赞/ 218 阅读
相关 模板模式 模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的... 小灰灰/ 2020年06月13日 05:46/ 0 赞/ 631 阅读
还没有评论,来说两句吧...