This is not a hugely difficult problem, but it is complex enough that we can see how different paradigms
approach the problems in different ways. The paradigms are all theoretically equivalent: that is, there is no
problem that one paradigm can solve that another can't solve. However, each paradigm is best suited to
address particular kinds of problems. It is easy to see these differences when you see the paradigms at work.
Imperative Programming Paradigm
The first computer programming languages were assembly languages. These are strange and low-level
beasts, and are arguably a paradigm unto themselves. Once computer programmers began to work in higher
levels of abstractions, the first batch of languages they invented were imperative. The word “imperative” here
comes from the human language structure: in the sentence, “Pick up that garbage,” the command, “Pick up,”
is the imperative. Imperatives are commands to perform some action. An imperative programming language
is a language that executes a system of instructions. If that sounds like a description of all programming
languages, it is because almost all the most popular programming languages are of this type, so you can go
through your entire career working in imperative (or near-imperative) code.
Java is fundamentally object oriented, but a lot of Java code can look quite imperative. Imperative
programming is a natural way for people to think about problems: even the pseudocode given in the
introduction was implicitly imperative. Because it is so natural, it is easy for a programmer to go from a gut
understanding of how to solve the problem into a series of instructions, entirely bypassing the design phase.
The result is imperative code. This approach works very well for simple scripts, especially when those scripts
are mimicking the interaction of a human being. This is why imperative languages such as perl, bash, and
Power Shell dominate shell scripting.
You can recognize imperative code in Java by looking at the methods. The dead giveaway is if the
codebase is dominated by static methods: if so, chances are you are seeing very imperative code. Imperative
code also lends itself to long methods with many arguments, and those methods will tend to call out to
other long methods with many arguments. What is going on is that the entire state of the application is being
passed around, including various signals for exceptional conditions. Everything needs to know everything
about everything in an imperative language.
For the imperative implementation, we will start by executing the schema creation. When that is done,
we will prepare all the statements that we will use throughout our execution. When that is done, we will load
the text stream. Then, for each line in the text stream, we will drop into a big if-else block. That block will be
responsible for determining where we are at in the processing, and how to process the current line. If we are
in the sonnets and the line is just a digit, we will create the new text in the database representing our new
sonnet. Otherwise, if the line is a year, that is signifying a start of a new text, and we will check to see if we are
in the sonnets, and we will handle things appropriately. Otherwise, if we are in a comment, we will scroll to a
comment. Otherwise, if it's the end, we'll note that we are definitely no longer in the sonnets. Otherwise,
we will break the line up into words and store each of the words into the database. The code for this is given
in Listing A-1.
Listing A-1. Imperative Database Loading