Now let's see how this whole thing works in practice by
using an example. We're going to consider a program that takes as
input a text file and produce it as output, a filtered
file. So basically it outputs a subset of the content of
this text file based on some filter. And we're going to have
four different types of filters. So the first one is
not filtering which means that the whole content of the text
file will be produced on the output. The second filter will output
only words that starts with t. So you'll take the text file
and simply ignore all of the words that do not start with
t. So in the output we'll have only those words that starts
with letter t. The third filter will produce in the output only
words that are longer than five characters. So all the other words
will be simply disregarded. And finally, the four filter will produce as
output only words in the text file that are palindromes, and in
case you don't know what a palindrome is, a palindrome is a word
that is the same whether you read it from left
to right or from right to left. For example, the
word kayak, you can read it in this direction, or
in this direction, and it's exactly the same word. So
let's see how this program could be implemented using a
strategy pattern. And let's do it for real as a
demo. What we're looking at here is the editor page
for Eclipse, open with the strategy pattern implementation for our example.
So what I'm going to do is that, I'm going to look at a
different part of implementation. And you will see that, you know, despite
the fact that it's slightly longer, it's really fairly simple, it's kind
of a straightforward implementation of what we just saw. As I just
said, what we are doing is basically building the strategy patterns that
allows for changing the strategies with which we're filtering an input file.
And we have different strategies, we'll look at those in detail, and
we said that the three participants for this pattern are the context,
the algorithm, which is the general interface and then the concrete
strategies, which are the concrete implementations of this algorithm. So let's
start by looking at the context. Which is this class here.
And as you can see it contains a reference at the current
strategy. We call this the check strategy, which is basically our
filter, and when the context is created by default it sets a
strategy to the old strategy. The old strategy is the one
that accepts all the input, so basically it doesn't filter out anything.
And we said that the context is the interface to the
outside world, right? So it has to provide the outside world
with a way of selecting the strategy, the specific algorithm to
be used, and it does that in this case by providing
this change strategy method. This method takes a strategy as input,
and simply replaces the current strategy with the one specified as
a parameter. And at this point, the context also will perform
the filtering. The filtering is pretty straightforward, so what it does is
that it opens a file that is passed as a parameter
so that this the file, the input file to be filtered. And
then it reads the file line by line and then splits
the lines into its composing words and then for each word in
each line, what it will do, it will basically invoke the
check method in the current strategy, which is basically the filtering method
and if the check method returns true, which basically means that
the word should be printed, it prints the word. Otherwise, it'll just
skip it. So basically the filter will return false for all the
words that have to be filtered out. Okay? This is the basic
way in which context works. Let's see how this is used in
our main method. The main method simply creates the context, reads the input file
from the arguments, and then what he does is simply as a
demonstration, it will perform the filtering using all the different filters. So
starting from the default one, which is the one that basically doesn't
do any filtering that reports all words, then it will switch to the
algorithm, that only considers the words that start with t, and
it will do that by invoking a change strategy and passing this
strategy as the argument, and then
performing the actual filtering through context.
And it will do exactly the same for the strategy that only
prints words that are longer than five and the one that
only prints words that are palindromes. So now let's look at the
actual algorithm. This is the interface, the algorithm interface. And you can
see that the only thing that the interface provides is this method,
which is the check method, that takes a string as input and will
return a boolean. So, basically, it's the boolean that we were seeing before.
The one that is true for the words that have to be printed
and false for the ones that have to be filtered out. Now, we have
all the different implementations of the algorithm, the simplest one is the all
algorithm, the simple return is always true, so all the words will be printed.
The second one starts with t, and again, without looking at the details
of implementations that don't really matter, what
it does is basically check that
the first character is t, and returns true in that case and
false otherwise. Similarly, for the LongerThan5
algorithm, also in this case, this
will implement the check strategy interface, and the check will be performed
by checking that the word is longer than five characters and returning
true in that case and false otherwise. And finally the Palindrome check
is a little more complicated, but basically it just checks whether the
word is a Palindrome and returns true in that case. Okay, so
as I said, it doesn't really matter too much what is the specific
implementations of these matters. What matters is that we have
a general interface for the algorithm and then any different concrete
implementations of the algorithm that implement different strategies. So again,
this allows you to change the behavior of your class without
changing class. So we have this context class that does
different things when the filter method in invoked, depending on what
is the current strategy. So the behavior of the class can
change dynamically, and it changes dynamically every time that we change
the strategy. At this point, the way this whole thing works should
be clear, so what we're going to do is that we're going to go to
our console, and we're actually going to run the strategy pattern and see
what happens. So here I have a file, it's called foo.txt. And if
we look at the content of foo, you can see that it
says that this is just a test to assess how well this program
performs when used on files of text. And since it checks for
palindromes, we will also insert one such word, level. Level is a palindrome,
because you can read it from both sides. Okay, so let's
see what happens when we run our code. So we're going to
run java pattern.strategy.StrategyPattern which is
our class, and we going to fetch
foo.txt as an input, and let's go back to the beginning
of the output to see what happened exactly. You can see
here that for the default strategy, which was the old strategy,
the whole file is printed, so every word is printed. This
is just a test to assess and so on and so forth,
as expected. For the filter that only prints words that
start with t, only words that start with t are printed,
again, as expected. Similarly, for the filter that only prints words
that are longer than 5, and finally for the one that prints
palindromes. And here you can see that we actually have two
because the way in which this is implemented we'll also consider
single letter words as palindromes because you can read them from
both sides. But you definitely will also have level in the output.
And in case you want to play with this code yourself, I
have made this code and also the implementation for examples of other
design partners available as a compressed archive. And the archive is accessible
through a URL that is provided in the notes for the cost.