Java Reference
In-Depth Information
If you are going to execute asynchronously, always execute asynchronously. If you are going to execute
synchronously, always execute synchronously. Don't mix synchronous and asynchronous styles. Don't
release Zalgo!
Build Complexity from Simple Parts
When we looked at Function.compose and Function.andThen , we built a function in one line that will give
you the integer whose value in decimal is “10” concatenated with the given value. So if you give it “0,” you get
“100.” If you give it “10,” you get “1010.” That's a nontrivial piece of logic, but we built it up by gluing together
three method references, and then we got a handle on a single function that performed that behavior.
In the same way that we build up methods by delegating to other methods, we can build up functions by
delegating to and wrapping other functions: find some way to perform the core logic, then prepend the function
with type conversions and argument manipulation, and then append the function with result conversions.
This approach lets us do what aspect-oriented programming was aiming at: you can wrap your simple
pieces of logic in handlers that perform additional processing without muddying up the core implementation
with ancillary details. This approach allows us to continue to write focused, limited, type-specific methods
that do one thing and do it well. These methods are easier to write and provide fewer nasty corners where
bugs can reside.
Most importantly, these simple parts are more apt to be reused. Code reuse is the second derivative of
code: 12 ; it is through code reuse that we not only accelerate our development, but accelerate the acceleration
of our development. The old acronym is “KISS”: “Keep It Simple, Silly,” and as developers, we KISS for speed.
Use Types and the Compiler to Your Advantage
The major advantage that languages such as Scala and Java have over all other postfunctional languages is
their type system. The type system will catch many of your errors. In our “Don't Release Zalgo” example, for
instance, we could have returned a Future type instead of delegating to the callback. That type would denote
that we execute asynchronously and would have given us pause when we went to execute synchronously.
Similarly, if you drive null out of your codebase, the Optional type denotes when a value may or may not
exist, ensuring that you handle the “may not exist” case more explicitly. It is easier for us to build up simple
types from complex ones when the compiler tells us if our types mismatch: it is easy to get lost in a chain of
andThen instances without compiler assistance.
There are other ways in which the compiler can help you out. For instance, if you declare a variable final ,
the compiler will ensure that you have it assigned before you use it. This is useful for ensuring that a variable is
assigned once (and only once) when you enter into an if/else block. Here is an example of that code:
final Function<String,Integer> converter;
if(str == null || str.isEmpty()) {
converter = s -> 0;
} else if(str.matches("\\d+")) {
converter = Integer::parseInt;
} else {
Function<String,String> eliminateNondigits = s -> {
return s.replaceAll("[^\\d]", "");
};
12 See http://blog.enfranchisedmind.com/2007/09/use-vrs-reuse-or-the-second-derivitive-of-
programming/ and http://blog.enfranchisedmind.com/2007/09/development-acceleration-the-second-
derivative-of-functionality/ for more on this.
 
Search WWH ::




Custom Search