Read also:
In this
part, we will re-write the application from first part
with respect to JSF+CDI style. Basically, we try to keep the strategy pattern,
and adjust the code to "support" the user interface from figure
below:
Before we
can shape this user interface, we need to adjust the source code of the strategy interface (ArchiverStrategy),
concrete strategy implementations (FastArchiverStrategy,
SlowArchiverStrategy)
and context
(Archiver)
accordingly.
Implement Strategy Interface
In our
example, the strategy interface is
represented by the ArchiverStrategy interface. This remains unchanged, since
we need to define the same contract:
public
interface ArchiverStrategy {
public void archive(String path,
List<String> files);
}
Implement Concrete Strategies
Our concrete strategies, FastArchiverStrategy
and SlowArchiverStrategy
will be modified as follows:
·
we
annotate them as request scoped CDI beans;
·
for FastArchiverStrategy, we remove the
constructor with arguments and we expose the compression field via getter
and setter;
·
for SlowArchiverStrategy, we remove the
constructor with arguments and we expose the compression, numFastBytes
and numPasses
fields via getters and setters;
Once we
accomplish these adjustments, the user
will be able to configure the archiver parameters:
@Named
@RequestScoped
public class
FastArchiverStrategy implements ArchiverStrategy {
private int compression;
public int getCompression() {
return compression;
}
public void setCompression(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);
}
}
@Named
@RequestScoped
public class
SlowArchiverStrategy implements ArchiverStrategy {
private int compression;
private int numFastBytes;
private int numPasses;
public int getCompression() {
return compression;
}
public void setCompression(int compression) {
this.compression = compression;
}
public int getNumFastBytes() {
return
numFastBytes;
}
public void setNumFastBytes(int numFastBytes)
{
this.numFastBytes = numFastBytes;
}
public int getNumPasses() {
return numPasses;
}
public void setNumPasses(int numPasses) {
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 +
")");
}
}
Notice that
these CDI managed beans represents now our algorithms implementations. By their
nature, managed beans are not meant for business logic, while algorithms code
may be considered business logic and may be really complex and may rely on
possibly expensive service/DAO operations, so, if you are in such cases, take
care of separating the business logic (maybe in a EJB bean).
Implement Context
Our context, Archiver,
will be modified as follows:
·
we annotate it as request scoped CDI bean;
·
we replace the helper methods (addFile()
and removeFile())
with getter and setter for the files field;
Via these
adjustments, the user will be able to select the files that will be part of the
archive. Of course, if you need to add the files one by one, or need to be able
to remove files, then you can rely on the helper methods combined with a view scoped
bean and the desired interface.
@Named
@RequestScoped
public class
Archiver {
private List<String> files;
public Archiver() {
this.files = new ArrayList<>();
}
public List<String> getFiles() {
return files;
}
public void setFiles(List<String> files)
{
this.files = files;
}
public void archive(String path, ArchiverStrategy
archiverStrategy){
archiverStrategy.archive(path, files);
}
}
Now, we can
shape the interface from figure above as:
<h:form>
Select files to archive:
<h:selectManyListbox
value="#{archiver.files}">
<f:selectItem
itemValue="foo.txt" itemLabel="foo.txt"/>
<f:selectItem
itemValue="buzz.txt" itemLabel="buzz.txt"/>
<f:selectItem
itemValue="fizz.txt" itemLabel="fizz.txt"/>
</h:selectManyListbox>
Compression: <h:inputText
value="#{slowArchiverStrategy.compression}"/>
Number Fast Bytes: <h:inputText
value="#{slowArchiverStrategy.numFastBytes}"/>
Number Passes: <h:inputText
value="#{slowArchiverStrategy.numPasses}"/>
<h:commandButton value="Slow"
action="#{archiver.archive('SOME_PATH', slowArchiverStrategy)}"/>
Compression: <h:inputText
value="#{fastArchiverStrategy.compression}"/>
<h:commandButton value="Fast"
action="#{archiver.archive('SOME_PATH', fastArchiverStrategy)}"/>
</h:form>
Supposing
that we have select the foo.txt and fizz.txt files, set
compression to 9, fast bytes to 128, passes to 10, and we pressed on the Slow button:
Slow
archiver for [foo.txt, fizz.txt] at compression level 9 (Fast Bytes: 128,
Passes: 10)
Or, if we
select all three files, set compression to 0 and press the Fast
button:
Fast
archiver for [foo.txt, buzz.txt, fizz.txt] at compression level 0
In the next part of this post we will discuss about strategy pattern in JSF source code.
Niciun comentariu :
Trimiteți un comentariu