Using callbacks to achieve extensibility
Use callback to achieve scalability
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.
Let's now consider another method of control inversion to parameterize a single operation, and transfer in control or error handling to a framework. Strictly speaking, this is a special case of the Rule mode: Because the interface is so simple, it presents different changes.
This pattern is based around the use of one or more callback methods that are invoked by a method that performs a workflow.
This mode is based on the use of more than one callback method, which is called by a method that executes the 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.
When working with low-level APIs, such as JDBC, I found this mode useful. The following example shows a simplified form of JDBC public classes. jdbctemplate is used in simple applications.Program, Which will be further discussed in chapter 9th.
JdbctemplateImplementsQuery ()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 implements a query () method, which uses an SQL query string as a parameter and implements a callback interface. This callback interface is called to set the query Generator for each row of the result set.
Public InterfaceRowcallbackhandler {
VoidProcessrow (resultset RS)ThrowsSqlexception;
}
TheJdbctemplate. 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:
The jdbctemplate. Query () method hides the details of a JDBC connection obtained from the call.Code, Create and use a statement to correctly release resources or even error events, as shown below:
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.exe cutequery (); 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 );
}
}
TheDatasourceutilsClass contains a helper method that can be used to close connections, catching and logging anySqlexceptionsEncountered.
The datasourceutils class contains a help method that can be used to close the connection, capture and record the occurrence of any sqlexceptions.
In this example,JdbcsqlexceptionExtendsJava. Lang. runtimeexception, Which means that calling code may choose to catch it, but is not forced. 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 inResultset, It will do calling code no good to catch it. This is clearly a programming error, andJdbctemplate'S behavior of logging the exception and throwing a runtime exception is logical (see discussion onError Handling-checked or unchecked exceptionsLater ).
In this example, jdbcsqlexception inherits java. Lang. runtimeexception, which means that the Code may be captured but not forcibly captured. This makes sense in the case of persistence. If, for example, a callback process tries to obtain a column value that does not exist in the resultset, it calls the code without capturing it. This is an obvious programming error. jdbctemplate records abnormal behavior and throws a runtime exception. It is logical (see the discussion about error handling and non-check exceptions later ).
In this case, I modeledRowcallbackhandlerInterface as an inner interface ofJdbctemplateClass. This interface is only relevant toJdbctemplateClass, so this is logical. Note that implementations ofRowcallbackhandlerInterface 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.
In this case, I imitatedRowcallbackhandlerInterface is an internal interface of the jdbctemplate class. This interface is only associated with jdbctemplate, so this is logical.
Note:RowcallbackhandlerThe implementation of interfaces may be internal classes (in the case of no importance, anonymous internal classes are suitable), or they may be standard and reusable classes, or a subclass of a class that is easy to use.
Consider the following implementation ofRowcallbackhandlerInterface to perform a JDBC query. Note that the implementation isn't forced to catchSqlexceptionsThat may be thrown in Extracting Column values from the result set:
Consider the following:RowcallbackhandlerThe implementation of the interface executes a JDBC query. Note that this implementation is not forced to capture and may be thrown when the column value of the result set is extracted.
Class Stringhandler Implements Jdbctemplate. rowcallbackhandler {
Private List 1 = New Vertex list ();
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:
This class will be used below:
Stringhandler SH =NewStringhandler ();
Jdbctemplate. Query ("Select forename from custmr", SH );
String [] forenames = Sh. getstrings ();
These three lines show how the code that usesJdbctemplateIs able to focus on the business problem, without concerning itself with the jdbc api. AnySqlexceptionsThrown will be handledJdbctemplate.
These three lines show that jdbctemplate can be used to focus on business issues without considering its own relationship with JDBC APIs. Any thrown sqlexceptions will be processed by jdbctemplate.
This pattern shouldn't be overused, but can be very useful. The following advantages and disadvantages indicate the tradeoffs involved:
This mode should not be abused, but it is very useful. The following shows the advantages and disadvantages:
Advantages:
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.
-
Framework classes can be executed to handle errors and release resources. This means that the error handling exception (required in JDBC) can be written only once, and the calling code is simpler. The more complicated the error handling and clearing calls,
-
The more attractive this approach is.
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 shoshould focus on.
-
The calling code does not need to handle low-level API details, such as JDBC. This is satisfactory, because such code has fewer bugs, and the application code must focus on this business problem.
-
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.
-
A function of the control flow (in the jdbctemplate. Query () example) can be used for a wide range of callback processing to execute different tasks. This is a good way to reuse code with low-level APIs.
Disadvantages:
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.
There is a saying that calling code processing to execute your own workflow is not intuitive, so the code may be difficult to understand and maintain, if there is no other choice here.
We need to create an object for the callback handler.
We need to create a callback object.
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.
In rare cases, the performance may degrade because the callback processing needs to be called through the interface. The above example can be ignored, compared with the JDBC operation time.
This pattern is most valuable when the callback interface is very simple. In the example, becauseRowcallbackhandlerInterface 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.
This mode is extremely valuable when the callback interface is simple. In this example, since the rowcallbackhandler Interface contains a single method, it is easy to implement, meaning that the selection of this implementation, such as anonymous internal classes, may be used to simplify the calling code.