Is
recommended to read first part
I of this post (at least be familiar with the example from the Decorator
pattern in plain code subsection).
Now, we
will implement the decorator pattern from Java EE perspective. More precisely
we will use CDI @Decorator and @Delegate to shape
in CDI style the example from the first part of this post.
·
@Decorator annotates the decorator classes
·
@Delegate annotates the delegate injection
point where the class to be decorated is injected
In order to
correlate the decorator with the object that you want to decorate we start by
reshaping the Hosting interface - the getHostingSubscription() is the
method that will be implemented by decorators for adding the extra
functionalities (extra items and prices):
public
interface Hosting {
public String getSubscriptionItems();
public int getSubscriptionPeriod();
public double getSubscriptionPrice();
public void setSubscriptionItems(String
items);
public void setSubscriptionPeriod(int period);
public void setSubscriptionPrice(double
price);
// the decorator will implement the below
method
public String getHostingSubscription();
}
First,
this interface is implemented by the object that follows to be decorated. This
is WebHosting:
public class
WebHosting implements Hosting {
private String items = "email account |
unlimited traffic";
private int period = 12;
private double price = 25.0;
@Override
public String getSubscriptionItems() {
return items;
}
@Override
public int getSubscriptionPeriod() {
return period;
}
@Override
public double getSubscriptionPrice() {
return price;
}
@Override
public void setSubscriptionItems(String items)
{
this.items = items;
}
@Override
public void setSubscriptionPeriod(int period)
{
this.period = period;
}
@Override
public void setSubscriptionPrice(double price)
{
this.price = price;
}
@Override
public String getHostingSubscription() {
return
items + "; " + period + " months" + "; $" +
price;
}
}
Further,
we can create decorators! A CDI decorator is annotated with @Decorator and can
be abstract. While
the @Decorator will
instruct the container that this class is a decorator, the instance that will
be decorated (the delegate injection point) is annotated with @Delegate and must
be:
·
an injected field
·
an initializer method parameter
·
a bean constructor method parameter
The
delegate type must be the interface implemented by the classes that you want to
be decorated, which in our case is Hosting - notice that
there is no need to implement all the @Delegate methods. The
container will inject any available instance of this interface into the corresponding
member, as below - this is the SEOExtraHosting decorator:
@Decorator
public
abstract class SEOExtraHosting implements Hosting {
@Any
@Inject
@Delegate
private Hosting hosting;
@Override
public String getHostingSubscription() {
hosting.setSubscriptionItems(hosting.getSubscriptionItems() + " |
" + "SEO");
hosting.setSubscriptionPrice(hosting.getSubscriptionPrice() + (7 *
5.0));
return
hosting.getHostingSubscription();
}
}
Another
decorator may look like this - the SSHExtraHosting decorator:
@Decorator
public
abstract class SSHExtraHosting implements Hosting {
private final int EXTRA_SSH_TAX = 3;
@Any
@Inject
@Delegate
private Hosting hosting;
@Override
public String getHostingSubscription() {
hosting.setSubscriptionItems(hosting.getSubscriptionItems() + " |
" + "SSH");
hosting.setSubscriptionPrice(hosting.getSubscriptionPrice()
+ (2 * 3.0) + EXTRA_SSH_TAX);
return hosting.getHostingSubscription();
}
}
Well, the
job is almost done! Final step requires some configurations. The decorators
should be declared in beans.xml in the same order that we want them
to be chained and applied (the first declared decorator is first applied, and
so on; e.g. in this case, the order is SEOExtraHosting, SSHExtraHosting), as
below (in our case the order is not relevant, but this is not always the case):
<?xml
version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
<decorators>
<class>beans.SEOExtraHosting</class>
<class>beans.SSHExtraHosting</class>
</decorators>
</beans>
Finally,
we can use our decorators in a CDI managed bean, as below:
@Named
@RequestScoped
public class
MyBean {
@Any
@Inject
Hosting hosting;
public void applyDecorators() {
System.out.println(hosting.getHostingSubscription());
}
}
This will
output: email
account | unlimited traffic | SEO | SSH; 12 months; $69.0
So, is
important to keep in mind that decorators can be enabled/disabled at deployment
time by editing the beans.xml configuration file and a decorator is
automatically applied to classes that implement the same interface. This can be "fixed" by using qualifiers!
In order to skip the declarations in the beans.xml configuration file, you can use @Priority and an @Interceptor.Priority value, as below:
In order to skip the declarations in the beans.xml configuration file, you can use @Priority and an @Interceptor.Priority value, as below:
@Priority(Interceptor.Priority.APPLICATION)
@Decorator
public
abstract class SEOExtraHosting implements Hosting {
...
@Priority(Interceptor.Priority.APPLICATION
+ 1)
@Decorator
public
abstract class SSHExtraHosting implements Hosting {
...
The order of invocation is dictated by the value of @Interceptor.Priority - lower value invoke first.
Niciun comentariu :
Trimiteți un comentariu