Week 10: Lab 6
Objectives
To learn the functional programming style using the new Java language syntax (λ-expression) and library (Stream APIs) by practicing refactoring of an old-fashioned code.
Refactoring
… is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior.
Its heart is a series of small behavior preserving transformations. Each transformation (called a “refactoring”) does little, but a sequence of transformations can produce a significant restructuring. Since each refactoring is small, it’s less likely to go wrong. The system is kept fully working after each small refactoring, reducing the chances that a system can get seriously broken during the restructuring.
Martin Fowler, https://refactoring.com/
Exercise One
Begin with the original program which is written in the “old”, pre-Java 8 style, with the aim of replacing the anonymous inner classes on lambda-expressions. This should have immediate consequences in reducing the number of source files which are required by the program.
If you are doing this lab in your cloned GitLab repository, the following
source code and data should already be in homeworks/hw6
:
The Assimilation
program simulates an attack by
The Borg, an evil collective
of cyborgs who force all those they deem “worthy” to submit to cybernetic
enhancement and integration into their “hive mind”.
The program accepts one command line argument: the name of the file containing a list of people to assimilate. For example:
java Assimilation star_trek.txt
Once you have successfully converted the original code into streams-based code using λ-expressions, the interfaces Selection.java and Operation.java will no longer be required.
Refactor the code of Assimilation.java
by employing lambdas and streams of Java 8. The code of Individual.java
and
Borg.java
do not require any change. In Assimilation.java
, first deal
with mankind listing and selection processes, originally in
Assimilation:65-66
and Assimilation:71-77
(lines of relevant code in
the source file are shown). Your solution should include stream creation from
list object mankind, and stream operations forEach()
and filter()
.
Think how to assign value to the consumer and predicate objects (in forEach
and filter
correspondingly). Once you’ve done with re-implementing the
selection, you will see that the auxiliary method cull()
(Assimilation:16-25
) is no longer used and can be removed.
Exercise Two
As the second step, re-implement using streams the conversion process
Individual -> Borg. Here, the stream operation is map(..)
, which is run
on a stream of individuals who passed the filter process. Think what
java.util.function.Function
is required here. Can it be initialised as a
lambda expression, or an existing method reference (which class and which
method?) can be used? (Hint: both are possible.) Think how the Borg objects
(which are the result of mapping individuals by map(Individual -> Borg)
)
can be collected back to a list for final merging with pre-existing Borg
(this part, Assimilation:90
, needs no change). This has to be a terminal
operation; more than one solution is possible. Similar in Exercise One, an
auxiliary method convert()
(Assimilation:27-33
) is no longer required
and can be removed.
Exercise Three (praxis postmortem)
Use git diff
command (learn how to use it if on the command line) to view
total changes in the original code whence you’ve done both exercises. If you run
macOS or Linux you can install the utility called sloccount
(or its analogs)
to intelligently count how many non-comment and non-empty lines of code a
program has. Use it to count number of lines in the original and final
solutions. All-in-all, two interfaces, two methods and most of the loops
gone! — significant reduction in the code size (in my solution, about 35%).