I've been experimenting with fluent API design. You can find the sources in part 4.
I've often been frustrated with the verbosity of Java I/O. Handling close with decorators got better with the introduction of the Closeable interface, but there's still a bit of boilerplate. This post describes a new fluent API to wrapper around the existing I/O API.
Goals
The goals of this API are:
- Reduce the amount of code required for I/O operations.
- Be extensible.
- Interoperate with the existing java.io API and any 3rd party frameworks that build on it (like Guava and Apache Commons I/O).
- Reduce the opportunities for resource leaks and poor error handling.
- Encourage good character data transcoding.
- Add some support for runtime exception handling.
- Be small, clean and easily understood.
Terminology
- Doctor: to revise, alter, or adapt in order to serve a specific purpose or to improve the material
- Die: any of various devices for cutting or forming material in a press or a stamping or forging machine
Traditional Java I/O
The following method replaces all the newlines in a UTF-8 text
input file with unix-style \n
newlines.
private static void transform(File src, File dest) throws IOException {
|
This code does its best to be bullet-proof. Even if a stream
constructor were to throw a RuntimeException
, the use of Closeable
s
ensures that the contract for the preceding streams is enforced and they
are closed properly. The downside: a load of extra lines and a nested
try-finally block.
Better closing
It is possible to leverage the Closeable
interface
to eliminate some of this code. Here is the method rewritten to use Closer
s
from the new API:
private static void closingTransform(File src, File dest) throws IOException {
|
The CoupledCloser
type just holds two Closer
instances. A Closer
just holds a reference to the last Closeable
passed to its using
method.
This code manages to eliminate a try-finally block. The calls are compressed onto fewer lines, but it is arguable whether this code is clearer.
Fluent I/O
Here is the transform method rewritten to use the new API:
private static void fluentTransform(File src, File dest) throws IOException {
|
To make the type system visible, here is the code broken down into individual calls:
private static void transformOp(File src, File dest) throws IOException {
|
This code is obviously way worse than just using the core API, so it should be clear why sticking to the fluent style is prefereable.
The doctor
method illustrates the main extensibility
mechanism in the API. By writing Doctor
interface
implementations, you can doctor stream types in any manner you see fit.
Since no I/O API can cover every conceivable stream decorator, this is a
necessary compromise to the fluent design pattern.
Related posts
- Part 1: introduction
- Part 2: extending the API
- Part 3: error handling
- Part 4: conclusions and downloads
Comments & criticism are welcome.
No comments:
Post a Comment
All comments are moderated