使用继承,子类中不须要实现那些重复的订票和庆祝团圆的代码了,避免了代码的重复;子类实现了不一样方式的回家方法,把它栓入(hook)到父类中去,实现了完整的回家过年的逻辑。html
package pattern.part1.chapter2.template; public abstract class HappyPeople { public void celebrateSpringFestival() { subscribeTicket(); travel(); celebrate(); } protected final void subscribeTicket() { //Buying ticket... System.out.println("Buying ticket..."); } protected abstract void travel(); protected final void celebrate() { //Celebrating Chinese New Year... System.out.println("Happy Chinese New Year!"); } }
package pattern.part1.chapter2.template; public class PassengerByTrain extends HappyPeople { @Override protected void travel() { //Travel by Train... System.out.println("Travelling by Train..."); } }
package pattern.part1.chapter2.template; public class PassengerByCoach extends HappyPeople { @Override protected void travel() { // Travel by Coach... System.out.println("Travelling by Coach..."); } }
package pattern.part1.chapter2.template; public class PassengerByAir extends HappyPeople { @Override protected void travel() { //Traveling by Air... System.out.println("Travelling by Air..."); } }
package pattern.part1.chapter2.template; public class HappyPeopleTestDrive { public static void main(String[] args) { HappyPeople passengerByAir = new PassengerByAir(); HappyPeople passengerByCoach = new PassengerByCoach(); HappyPeople passengerByTrain = new PassengerByTrain(); System.out.println("Let's Go Home For A Grand Family Reunion...\n"); System.out.println("Tom is going home:"); passengerByAir.celebrateSpringFestival(); System.out.println("\nRoss is going home:"); passengerByCoach.celebrateSpringFestival(); System.out.println("\nCatherine is going home:"); passengerByTrain.celebrateSpringFestival(); } }
Let's Go Home For A Grand Family Reunion... Tom is going home: Buying ticket... Travelling by Air... Happy Chinese New Year! Ross is going home: Buying ticket... Travelling by Coach... Happy Chinese New Year! Catherine is going home: Buying ticket... Travelling by Train... Happy Chinese New Year!
回调表示一段可执行逻辑的引用(或者指针),咱们把该引用(或者指针)传递到另一段逻辑(或者方法)里供这段逻辑适时调用。
回调在不一样语言有不一样的实现,例如,在C语言里常常使用函数指针实现回调,在C#语言里使用代理(delegate)实现,而在Java语言里使用内部匿名类实现回调。java
package pattern.part1.chapter2.callback; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import static org.easymock.EasyMock.anyObject; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; public class ConnectionUtils { //we create a mock connection and a preparedStatement using easymock to show on how callback works public static Connection getConnection() throws SQLException { Connection conn = createMock(Connection.class); PreparedStatement statement = createMock(PreparedStatement.class); expect(statement.executeQuery()).andReturn(null); expect(conn.prepareStatement((String) anyObject())).andReturn(statement); replay(conn); replay(statement); return conn; } }
package pattern.part1.chapter2.callback; import java.sql.ResultSet; public interface ResultSetHandler<T> { public T handle(ResultSet rs); }
package pattern.part1.chapter2.callback; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class SimpleJdbcQueryTemplate { public <T> T query(String queryString, ResultSetHandler<T> rsHandler) { Connection connection = null; PreparedStatement stmt = null; try { connection = ConnectionUtils.getConnection();//get a db connection. stmt = connection.prepareStatement(queryString); ResultSet rs = stmt.executeQuery(); return rsHandler.handle(rs); } catch (SQLException ex) { closeStatement(stmt); //close the statement stmt = null; releaseConnection(connection); //release connection connection = null; throw new RuntimeException("An sql exception occurred.", ex); //rethrow a runtime exception } finally { closeStatement(stmt); //close the statement releaseConnection(connection); //release connection } } private void releaseConnection(Connection connection) { if (connection != null) { try { connection.close(); //close the connection or put it back to the connection pool } catch (SQLException ex) { //todo handle SQLException } catch (Throwable ex) { //todo handle other exception } } } private void closeStatement(Statement stmt) { if (stmt != null) { try { stmt.close(); } catch (SQLException ex) { //todo handle SQLException } catch (Throwable ex) { //todo handle other exception } } } }
package pattern.part1.chapter2.callback; import java.sql.ResultSet; import static pattern.Asserts.assertTrue; public class TemplateTestDrive { public void testTemplate() { boolean called = new SimpleJdbcQueryTemplate(). query("select * from db", new ResultSetHandler<Boolean>() { public Boolean handle(ResultSet rs) { //logical to resolve query result... return Boolean.TRUE; } }); //to verify result... assertTrue(called); } public static void main(String[] args) { new TemplateTestDrive().testTemplate(); } }
正常结束sql
这一章咱们在介绍模板方法模式以前介绍了DRY原则。重复的代码会带来维护的噩梦,DRY是一名优秀的软件开发人员必须恪守的原则之一。此原则看上去很简单,其实实现起来一点也不简单。在如下章节里,咱们将继续碰到重复代码的“臭味”,咱们会介绍更多的模式来防止代码重复。架构
在本章,为了解决回家过年的问题,咱们使用了模板方法模式。模板方法模式很是简单,相信很多人在学习模式以前早就开始使用了。模板方法模式能够解决某些场景中的代码冗余问题,但也可能引入了类的泛滥问题,随后咱们介绍了如何结合使用回调避免类的泛滥。使用回调能够避免类的泛滥,这并非表示咱们将使用带有回调的模板方法模式来替换全部的不带回调的模板方法模式,若是回调实现的接口较多,代码较为复杂时,把这些代码挤在一块儿会引发阅读问题。app
在如下章节读者能够继续看到,若是需求变得更为复杂,咱们就得须要更加灵活的设计,使用模板方法模式不可以成为新的复杂需求的解决方案,咱们会在策略模式等相关章节继续以该问题为例展开深刻讨论。ide