Read also:
JSF and Composite design pattern - part I (plain code)
In the first part of this article you saw an example of implementing the composite design pattern in plain Java code. Further, we will re-write that example in order to provide a JSF+CDI implementation. Keep in mind that this is just an approach, not the only approach! The interface of our application will look like in figure below:
JSF and Composite design pattern - part I (plain code)
In the first part of this article you saw an example of implementing the composite design pattern in plain Java code. Further, we will re-write that example in order to provide a JSF+CDI implementation. Keep in mind that this is just an approach, not the only approach! The interface of our application will look like in figure below:
So, let's
consider each artifact of the plain code example and let's see how we will
modify it.
Implement Base Component
In our
example, the base component is
represented by the Car interface. This remains unchanged, since we need to
define the same contract:
public
interface Car {
public void paint(String color);
}
Implement Leaf Objects
Our leaf objects, Logan, Volvo
and Mazda
will be modified as follow:
·
we annotate them as request scoped CDI beans;
·
we provide a toString() method ;
·
we rename them as LoganBean, VolvoBean
and MazdaBean
(this is just to look nice)
Since we
need to expose the leaf objects in
page via EL, we need to transform them in CDI managed beans. Further, the list
with cars to paint (instances of LoganBean, VolvoBean and MazdaBean)
should be displayed in the client interface. In order to accomplish this, we
override the toString()
method, which will provide a human readable string for the leaf instances. Moreover, when we click on a car from the list, we
want to be removed from the paint list, and for this, we need a converter to
convert the submitted strings into a Car instances. A convenient approach
will consist in using the OmniFaces, SelectItemsConverter
which also requires a good toString() implementation. Is true that our
implementation is not a good/real one, since we simply replace the default fqn@hashcode with car_name{hashcode}, but it will do the job
in our example - suppose that the hashcode is some kind of car ID. So, we will obtain:
@Named
@RequestScoped
public class
LoganBean
implements Car, Serializable
{
@Override
public void paint(String color) {
System.out.println("Logan (" + this
+ ") paint color: " + color);
}
@Override
public String toString() {
return
"Logan {" + this.hashCode() + "}";
}
}
@Named
@RequestScoped
public class
VolvoBean
implements Car, Serializable
{
@Override
public void paint(String color) {
System.out.println("Volvo (" + this
+ ") paint color: " + color);
}
@Override
public String toString() {
return
"Volvo {" + this.hashCode() + "}";
}
}
@Named
@RequestScoped
public class
MazdaBean
implements Car, Serializable
{
@Override
public void paint(String color) {
System.out.println("Mazda (" + this
+ ") paint color: " + color);
}
@Override
public String toString() {
return "Mazda {" + this.hashCode() + "}";
}
}
Implement Composite
The composite object will be modified as
follows:
·
we annotate it as a view scoped CDI beans;
·
we provide getter and setter methods for the Car
list;
·
we adjust the remove() helper method to
work with valueChangeListener;
·
rename the PaintCar as PaintCarBean
(just to look nice).
The composite object contains group of leaf objects (Car) that should be
visible in the end-user interface. Moreover, the end-user should be able to
add/remove leaf objects from the list,
so we need to transform the composite object into a CDI managed bean. Since the
list should be maintained during multiple request in the same view, we need a
view scoped managed bean. In order to expose the list of cars, which is private,
we need the corresponding getter and setter. Finally, since we choose to use a <h:selectOneListbox>
with valueChangeListener
for removing the clicked cars, we need to re-write the remove() helper method
accordingly:
@Named
@ViewScoped
public class
PaintCarBean
implements Car, Serializable
{
//collection of cars
private List<Car> cars = new ArrayList<>();
@Override
public void paint(String color) {
System.out.println("Color: " +
color);
for (Car car : cars) {
car.paint(color);
}
}
//adding car
public void add(Car car) {
System.out.println("Adding car: " +
car);
this.cars.add(car);
}
//removing car
public void remove(ValueChangeEvent event) {
System.out.println("Removing car: " + event.getNewValue());
this.cars.remove((Car)event.getNewValue());
}
//removing all the cars
public void clear() {
System.out.println("Clearing all the
cars!");
this.cars.clear();
}
public List<Car> getCars() {
return
cars;
}
public void setCars(List<Car> cars) {
this.cars = cars;
}
}
Now, we can
shape the interface from figure above as:
<h:body>
Available cars:
<h:form>
<h:selectOneListbox
converter="omnifaces.SelectItemsConverter"
onchange="this.form.submit();" valueChangeListener="#{paintCarBean.remove}">
onchange="this.form.submit();" valueChangeListener="#{paintCarBean.remove}">
<f:selectItem
itemLabel="Click on the car to remove ..." noSelectionOption="true"/>
<f:selectItems
value="#{paintCarBean.cars}"
var="t" itemLabel="#{t}"
itemValue="#{t}"/>
</h:selectOneListbox>
</h:form>
<hr/>
<h:form>
Add
car:<br/>
<h:commandButton value="Add Logan
Car" action="#{paintCarBean.add(loganBean)}"/>
<h:commandButton value="Add Mazda
Car" action="#{paintCarBean.add(mazdaBean)}"/>
<h:commandButton value="Add Volvo Car"
action="#{paintCarBean.add(volvoBean)}"/>
<hr/>
Paint
cars:<br/>
<h:commandButton value="Paint cars
red" action="#{paintCarBean.paint('red')}"/>
<h:commandButton
value="Paint cars blue" action="#{paintCarBean.paint('blue')}"/>
</h:form>
</h:body>
After adding/removing
some cars and pressing the Paint cars red/blue buttons, you will see
an output as in the below sample:
Adding car:
Logan {920499440}
Adding car:
Mazda {686360510}
Adding car:
Mazda {784544289}
Adding car:
Volvo {2043446353}
Adding car:
Logan {480920299}
Removing
car: Mazda {784544289}
Removing
car: Logan {920499440}
Color: red
Mazda (Mazda
{686360510}) paint color: red
Volvo (Volvo
{2043446353}) paint color: red
Logan (Logan
{480920299}) paint color: red
Adding car:
Mazda {534814168}
Adding car:
Mazda {1979975711}
Color: blue
Mazda (Mazda
{686360510}) paint color: blue
Volvo (Volvo
{2043446353}) paint color: blue
Logan (Logan
{480920299}) paint color: blue
Mazda (Mazda
{534814168}) paint color: blue
Mazda (Mazda
{1979975711}) paint color: blue
Of course,
you can think to shape the interface differently. You just have to keep in mind
to not alter the composite design pattern meaning.
In the final
part of this post, we will discuss about how the composite pattern is used
internally in JSF source code.
Niciun comentariu :
Trimiteți un comentariu