This time example comes from Adobe, developer of Flex and ActionScript programming RIA language for their flash runtime. This interface represents a simple Ajax-style callback for an asynchronous operation (full source code):
* This interface provides the contract for any service
* that needs to respond to remote or asynchronous calls.
public interface IResponder
* This method is called by a service when the return value
* has been received.
datais typed as Object, it is often
* (but not always) an mx.rpc.events.ResultEvent.
* This method is called by a service when an error has been received.
infois typed as Object it is often
* (but not always) an mx.rpc.events.FaultEvent.
Now, looks innocent isn’t. But look carefully into the comments… Let me put in bold the offending logic there:
info is typed as Object it is often (but not always) an...
Now, is it not great :) I don’t know what prevented Adobe to have a strongly typed contract here - backward compatibility or the case of premature design for extension.
Proponents of this style will argue the following:
- The developer almost always knows what type to expect so the manual cast or dynamic method call is fine and usually safe
- You should always write unit tests to test for the expected type
Again, I don’t know what is more absurd is the fact that you now need to write unit tests for the compiler logic (how else I should say that?) or the fact that you, the developer, is expected now to remember what types to use where… (and don’t forget to keep your comments in the code up to date with expected type information – or else!). And, of course, you have no idea what exceptions this code should through in case of any errors occurred in the callback logic itself.
Let me give you a counter example from GridGain. Here is the method signature from Grid interface that allows you to execute the grid task having the class of that task (it will be auto-deployed if it wasn’t deployed already):
public <T, R> GridTaskFuture<R> execute(Class<? extends GridTask<T, R>> taskCls, @Nullable T arg)
This signature provides wells of type safety with just a few extra characters in the signature saving your potentially hundreds of lines in unit testing and headaches of crashing application in runtime. Here are the contracts that are automatically enforced by the compiler and supported by type-safe refactoring:
- Grid task expects two parameters of types <T> and <R> - type of the task’s argument and type of the task’s result value
- Method execute(…) returns task future that will contain result value of type <R>
- execute(…) method accepts execution argument of type <T>
- execute(…) method accepts any subclass of GridTask<T, R> interface
- Method execute() throw GridException to indicate error conditions and the called must provide handling logic
- Parameter arg is nullable.
To understand the benefits of this approach try to imagine amount of unit tests and comments you will have to write to maintain the same type invariant… Strongly typed approach truly saves massive amount of work and time. Think about it…