The GoF (Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides) book describes this pattern as a pattern that "Defines a set of encapsulated algorithms that can be swapped to carry out a specific behaviour."
The above
diagram (object strucutural) can be explained in a few words: in the strategy pattern, multiple
algorithms are represented as concrete strategy
classes, and they share a common strategy
interface. A context object contains
a reference to a strategy. In order
to obtain different behaviors we change the context's,
strategy. These behaviors are
different, but they affect data from the context.
So, the
strategy pattern (also known as policy pattern) is especially used when
multiple versions/variations of an algorithm are required. Basically, we own
multiple algorithms for a specific task (e.g. sorting an array) and we allows
the client to choose the desired implementation to be used at runtime (e.g. in
Java, we can provide multiple implementations of Comparator, and we pass the
desired implementation as parameter to the Arrays#sort(T[] a, Comparator<?
super T> c) method).
Notice that
the strategy pattern is very much like the state pattern. In the state pattern,
the context contains state as instance variable and we have
multiple tasks (jobs) whose implementation can be dependent on the state. By the other hand, in the
strategy pattern, the strategy represents
the argument of a method and context
object doesn’t have any variable to store it.
For our
example, we will try to implement a simple application for archiving files. We
start with the strategy interface,
which in our case contains the declaration of a method used for archiving files,
ArchiverStrategy:
public
interface ArchiverStrategy
{
public void archive(String path,
List<String> files);
}
Further, we
provide some implementation of this interface. Practically, we implement the
algorithms for creating archives. Let's suppose that we have two such algorithms,
one for fast archiving (FastArchiverStrategy),and one for slow
archiving (SlowArchiverStrategy):
public class
FastArchiverStrategy implements ArchiverStrategy {
private final int compression;
public FastArchiverStrategy(int compression) {
this.compression = compression;
}
@Override
public void archive(String path,
List<String> files) {
System.out.println("Fast archiver for
" + files + " at compression level " + compression);
}
}
public class
SlowArchiverStrategy implements ArchiverStrategy {
private final int compression;
private final int numFastBytes;
private final int numPasses;
public SlowArchiverStrategy(int compression,
int numFastBytes, int numPasses) {
this.compression = compression;
this.numFastBytes = numFastBytes;
this.numPasses = numPasses;
}
@Override
public void archive(String path,
List<String> files) {
System.out.println("Slow archiver for
" + files + " at compression level " +
compression + " (Fast Bytes: " + numFastBytes + ", Passes: " + numPasses + ")");
compression + " (Fast Bytes: " + numFastBytes + ", Passes: " + numPasses + ")");
}
}
Since the algorithms
are ready, we can go further and implement the archiver (context), which will require the algorithm used for archiving task
in the archive()
method, as below:
public class
Archiver {
private final List<String> files;
public Archiver() {
this.files = new ArrayList<>();
}
public void addFile(String file){
this.files.add(file);
}
public void removeFile(String file){
this.files.remove(file);
}
public void archive(String path,
ArchiverStrategy archiverStrategy){
archiverStrategy.archive(path, files);
}
}
Now, we can
perform a simple test:
Archiver
archiver = new Archiver();
archiver.addFile("foo.txt");
archiver.addFile("buzz.txt");
archiver.archive("SOME_PATH_1",
new FastArchiverStrategy(0));
archiver.archive("SOME_PATH_2",
new SlowArchiverStrategy(9, 128, 10));
The output
will be:
Fast
archiver for [foo.txt, buzz.txt] at compression level 0
Slow
archiver for [foo.txt, buzz.txt] at compression level 9 (Fast Bytes: 128,
Passes: 10)
In the nextpart of this post we will re-write this example in JSF+CDI style.
Niciun comentariu :
Trimiteți un comentariu