RxJava, short for “Reactive Java“, the Java implementation of the
“ReactiveX” specification, is sometimes used
to facilitate asynchronous or concurrent programming in Java, particularly in
code from Netflix, its original implementor. It enjoys some popularity in the
realm of server backend programming, and there appears to be surprisingly
little criticism of it.
“ReactiveX” is an extension of the observer
pattern and perhaps works well
in realms where that pattern is actually used, such as GUIs. But nobody in
their right mind writes GUIs in Java anymore outside a single mobile platform.
RxJava is instead purported to instead be a solution to asynchronous tasks on
the server, something for which the observer model is an extremely poor fit.
RxJava appears to draw people in on the basis of pretty diagrams that make it
look easy to work with and vacuous conceptions of universality. In reality,
outside of a very, very narrow space where it fits well, it brings far more
harm than good.
Encapsulation, in one form or another, is nearly universal in large-scale
programming across languages. Isolating the code which can access certain state
or data makes it easier to maintain and easier to reason about what impact
changes to that state could have. In object-oriented languages, the unit of
encapsulation is the class, and encapsulation is accomplished by making
instance variables private and exposing a set of well-defined operations on
that state in the public API.
JavaBean properties (a.k.a. “bean properties” or simply “getters and setters”)
arose when the point about making instance variables private was applied
universally without paying attention to the “well-defined operations” point.
Bean properties, particularly within an internal code-base or narrowly deployed
library, are nothing more than inconvenient public variables.
In the two decades of its existence, the Java programming language has gone
through quite a few changes. Even more so, however, have the overall approaches
to design and architecture in the Java ecosystem. I have found that these can
by and large be separated into four generations which typically cut over
relatively abruptly, immediately giving large code-bases of prior generations
an archaic and poorly-designed feel.
Mundane Java has two types of character-type literals: character literals,
enclosed in single-quotes, and string literals, enclosed in double-quotes.
1/* Character literal, enclosed in single-quotes */
2char ch = 'c';
3/* String literal, enclosed in double-quotes */
4String s = "This is a string.";
One of the first things not backwards-compatible with Java that a new Groovy
user may notice is that single-quotes now delimit strings as well.
Suppose that we have a stateless but computationally-intensive piece of code
involving a lot of function calls, which is for some reason written in Groovy.
SlowFib.groovy
1package gl.lin
2
3class SlowFib {
4 static int fib(int n) {
5 n <= 1? n : fib(n-1) + fib(n-2)
6 }
7}
Obviously, the algorithm here is pretty much the worst one possible — it has
exponential run-time, but calculating the Fibonacci sequence can be calculated
in linear time. But imagine for the time being that this is real code. The
important thing is that this code makes lots of function calls. Also
important is how the function is stateless — it accesses no data outside of
its locals, and is non-virtual due to its static
ness (ie, it could be called
with one instruction were this native code).
Now say we want to write a program that maps a sequence of input integers to
the corresponding Fibonacci number. A simple single-threaded implementation is
below.
The private
access modifier is arguably the most important in the context of
software engineering in Java, and more so in Groovy where everything is
public
(sort of) by default. By declaring
something private
, the programmer can be certain that no code outside the
current lexical scope can see the member; thus, there will never be any
external dependencies on its presence or behaviour, allowing it to be easily
changed or removed without widespread consequences. Of course, it could be
expected of Groovy to merely parse and discard the private
keyword,
substituting it with the “implicit-public” access modifier instead.
Fortunately, it doesn’t do that.
It does something much worse.
Groovy has an entire three different types of type-casts that can occur. The
first two are inherited from Java, albeit with substantially more permissive
semantics.
One of the most obvious ways with which Groovy tries to “reduce” boiler-plate
is to make all members (both variables and methods) of any class public by
default, as opposed to Java in which members are package-private by
default. While this is already a questionable design decision at best, an
interesting facet of the implementation is that implicitly public member
variables are private, though another Groovy feature masks this detail when
operating within Groovy.
We’ll start with a Groovy class that just has some members of varying types and
access modifiers. You can compile these files yourself by dropping them into
the base project.
In the past two
weeks, we’ve looked at how horribly
Groovy handles something as simple as function call arguments. There is yet one
more horror Groovy brings to the table: its conception of named arguments.
Named arguments (also known as “keyword arguments”, or just “kwargs”, in some
contexts) allow the programmer to specify arguments to a function by name,
rather than by position, enhancing readability and in many cases writability,
as the meaning of each argument is clarified at use site. It also has the
advantage of allowing some arguments to be specified without specifying the
preceding ones, in the case of arguments which also have defaults. Examples of
languages which support this concept are Python and OCaml.
As mentioned last week, one of the main reasons Java has method overloading was
to emulate optional arguments, albeit verbosely. In the below example, our
multiple definitions of doSomething
allow it to be called with any prefix of
its argument list, the other parameters “defaulting” to something that the
implementor considered reasonable.