Using Callbacks to Achieve Extensibility

Using Callbacks to Achieve Extensibility

利用回调完成可扩展性

Let's now consider another use of "inversion of control" to parameterize a single operation, while moving control and error handling into a framework. Strictly speaking, this is a special case of the Strategy design pattern: it appears different because the interfaces involved are so simple.

让我们现在考虑控制反转的另外一种用法去参数化单个操作,在移入控制或错误处理到一个框架中。严格的说,这是策略模式的一种特殊情况:由于接口涉及的如此简单是的它呈现不同的变化。

This pattern is based around the use of one or more callback methods that are invoked by a method that performs a workflow.

这个模式基于一个以上回调方法的使用,它被一个执行工作流的方法调用。

I find this pattern useful when working with low-level APIs such as JDBC. The following example is a stripped down form of a JDBC utility class, JdbcTemplate, used in the sample application, and discussed further in Chapter 9.

当与低级别的api工作时,例如jdbc,我发现这个模式是有用的。下面的例子是jdbc公共类的一个精简形式,JdbcTemplate,使用与在简单的应用程序中,在第9章将被进一步的讨论。

JdbcTemplate implements a query() method that takes as parameters a SQL query string and an implementation of a callback interface that will be invoked for each row of the result set the query generates. The callback interface is as follows:

jdbcTemplate实现了一个query()方法,它将一个sql查询字符串做为参数并且实现了一个回调接口。这个回调接口将被调用为结果集的每一行设置查询生成器。

public interface RowCallbackHandler {
void processRow(ResultSet rs) throws SQLException;
}

The JdbcTemplate.query() method conceals from calling code the details of getting a JDBC connection, creating and using a statement, and correctly freeing resources, even in the event of errors, as follows:

JdbcTemplate.query()方法隐藏了从调用得到一个jdbc连接的详细代码,创建和利用一个statement,正确的释放资源,甚至错误的事件,如下:

public void query(String sql, RowCallbackHandler callbackHandler)
throws JdbcSqlException {

Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
con = <code to get connection>
ps = con.prepareStatement (sql);
rs = ps.executeQuery(); while (rs.next()) {
callbackHandler.processRow(rs);
} rs.close();
ps.close();
} catch (SQLException ex) {
throw new JdbcSqlException("Couldn't run query [" + sql + "]", ex);
}
finally {
DataSourceUtils.closeConnectionIfNecessary(this.dataSource, con);
}
}


The DataSourceUtils class contains a helper method that can be used to close connections, catching and logging any SQLExceptions encountered.

DataSourceUtils类包含了一个帮助方法能被用于关闭连接,捕获和记录任何sqlexceptions的发生。

In this example, JdbcSqlException extends java.lang.RuntimeException, which means that calling code may choose to catch it, but is not forced to. This makes sense in the present situation. If, for example, a callback handler tries to obtain the value of a column that doesn't exist in the ResultSet, it will do calling code no good to catch it. This is clearly a programming error, and JdbcTemplate's behavior of logging the exception and throwing a runtime exception is logical (see discussion on Error Handling – Checked or Unchecked Exceptions later).

在这个例子中,JdbcSqlException继承了java.lang.RuntimeException,那意味者调用代码可能会选择捕获它,但是并没有强迫去捕获。在持久化的情形中这是有意义的。如果,举个例子,一个回调处理试着去获得在ResultSet中不存在的列值,它将调用代码而没有捕获它。这是明显的编程错误,jdbcTemplate记录异常的行为和抛一个运行时异常是符合逻辑的(在后面看关于错误处理检查和非检查异常的讨论)。

In this case, I modeled the RowCallbackHandler interface as an inner interface of the JdbcTemplate class. This interface is only relevant to the JdbcTemplate class, so this is logical. Note that implementations of the RowCallbackHandler interface might be inner classes (in trivial cases, anonymous inner classes are appropriate), or they might be standard, reusable classes, or subclasses of standard convenience classes.

在这种情形下,我模仿了RowCallbackHandler 接口做为JdbcTemplate类的内部接口。这个接口仅仅与JdbcTemplate相关联,所以这是合乎逻辑的。

注意RowCallbackHandler 接口的实现可能会是内部类(在不重要的情形下,匿名内部类是合适的),或者他们可能是标准,可重用的类,或者标准使用方便的类的子类。

Consider the following implementation of the RowCallbackHandler interface to perform a JDBC query. Note that the implementation isn't forced to catch SQLExceptions that may be thrown in extracting column values from the result set:

考虑下面RowCallbackHandler 接口的实现同执行一个jdbc查询。注意这个实现并没有被强迫去捕获可能被抛出在提取结果集的列值的时候的异常。

class StringHandler implements JdbcTemplate.RowCallbackHandler {
private List 1 = new LinkedList();
public void processRow(ResultSet rs)throws SQLException {
1.add(rs.getString(1));
}
public String[] getStrings() {
return (String[]) 1.toArray(new String[1.size()]);
}
}

This class can be used as follows:

这个类将被在下面用到:

StringHandler sh = new StringHandler();
jdbcTemplate.query("SELECT FORENAME FROM CUSTMR", sh);
String[] forenames = sh.getStrings();

These three lines show how the code that uses the JdbcTemplate is able to focus on the business problem, without concerning itself with the JDBC API. Any SQLExceptions thrown will be handled by JdbcTemplate.

这3行显示了利用JdbcTemplate可以集中在业务问题上,不需要考虑它自己与jdbc api的关系。任何抛出的SQLExceptions将被jdbcTemplate处理。

This pattern shouldn't be overused, but can be very useful. The following advantages and disadvantages indicate the tradeoffs involved:

这个模式不应该被滥用,但是是非常有用的。下面展现了这个优点和缺点:

Advantages:

优点:

  • The framework class can perform error handling and the acquisition and release of resources. This means that tricky error handling (as is required using JDBC) can be written once only, and calling code is simpler. The more complex the error handling and cleanup involved, the more attractive this approach is.

  • 框架类能被执行错误处理和获得资源的释放。这意味处理错误异常(在jdbc中被要求)能被仅仅写一次,调用代码是更简单。错误处理和清理调用越复杂,

  • 这个途径也越有魅力。

  • Calling code needn't handle the details of low-level APIs such as JDBC. This is desirable, because such code is bug prone and verbose, obscuring the business problem application code should focus on.

  • 调用代码不需要处理低级api详细情况,例如jdbc。这是令人满意的,因为这样的代码bug比较少,模糊了这个业务问题应用代码必须聚焦的。

  • The one control flow function (JdbcTemplate.query() in the example) can be used with a wide variety of callback handlers, to perform different tasks. This is a good way of achieving reuse of code that uses low-level APIs.

  • 控制流的一个功能(在JdbcTemplate.query()例子中)能被用于一个中类繁多的回调处理,去执行不同的任务。这是用低级别的api完成代码重用的一个好方式。

Disadvantages:

缺点:

  • This idiom is less intuitive than having calling code handle execution flow itself, so code may be harder to understand and maintain if there's a reasonable alternative.

  • 有句话说调用代码处理执行自己的工作流不直观,所以代码可能很难理解和维护,如果这里没有其他的选择。

  • We need to create an object for the callback handler.

  • 我们需要去创建一个回调对象。

  • In rare cases, performance may be impaired by the need to invoke the callback handler via an interface. The overhead of the above example is negligible, compared to the time taken by the JDBC operations themselves.

  • 在极少数情形下,性能可能下降,由于需要被调用回调处理通过接口。上面的例子是可以忽略的,与jdbc自己操作的时间相比。

This pattern is most valuable when the callback interface is very simple. In the example, because the RowCallbackHandler interface contains a single method, it is very easy to implement, meaning that implementation choices such as anonymous inner classes may be used to simplify calling code.

这个模式是极其有价值的,当回调接口是简单的。在这个例子中,由于RowCallbackHandler接口包含了单个方法,它很容易去实现,意味着这个实现的选择例如匿名内部类可能被用于去简化调用代码。

posted @ 2012-11-24 23:23  sqtds  阅读(188)  评论(0编辑  收藏  举报