Facade is a
Structural Design Pattern with the following object structural:
The GoF (Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides) book describes this pattern as “
providing a unified interface to a set of interfaces in a subsystem.”
Mainly the
Facade design pattern is meant to hide a complex code (as a system/subsystem
code) and to provide an unique entry point for it via an interface. There are
several purposes for implementing this design pattern, and one of them is for
providing a rough‐grained access to available services. Services are combined
in a business logic, and form a "unit of work" easy to access.
For
example, let's suppose that we want to find out the final price of a product
based on the following logic:
·
check the product availability and get the
product initial price
·
check the discount availability and apply a
discount
·
check the delivery availability and apply
delivery taxes
For each
of these tasks we have a separate stateless session bean, as follows (these are
the EJBs services to combine):
·
check the product availability and get the
product initial price - InventoryProduct:
@Stateless
public class
InventoryProduct {
public boolean productIsAvailable(String
product) {
// some
business logic here
return
true;
}
public int productCost(String product) {
// some
business logic here
return
120; // initial product cost is $120
}
}
·
check the discount availability and apply a
discount - DiscountProduct:
@Stateless
public class
DiscountProduct {
public boolean discountPeriodOfTheYear(){
// some
business logic here
return true;
}
public int applyDiscount(String product){
// some business logic here
return 100; // after the discount the product
price is $100
}
}
·
check the delivery availability and apply
delivery taxes - DeliveryProduct:
@Stateless
public class
DeliveryProduct {
public boolean freeDeliveryPeriodOfTheYear() {
// some business logic here
return false;
}
public int taxDelivery(int price){
// some
business logic here
return
103; // for $100 we add $3 delivery costs
}
}
Further
these three services should be used to create the below business logic (this is
the "unit of work"):
public int
orderProduct(String product) {
if
(inventoryProduct.productIsAvailable(product)) {
int
price = inventoryProduct.productCost(product);
int discount_price =
discountProduct.discountPeriodOfTheYear() ?
discountProduct.applyDiscount(product) : price;
int final_price =
deliveryProduct.freeDeliveryPeriodOfTheYear() ? discount_price :
deliveryProduct.taxDelivery(discount_price);
return final_price;
}
return -1;
}
We take
this method and place it in another stateless session bean named, FacadeProduct.
Basically, this bean is the design pattern implementation. Here, we inject our
services and define the orderProduct(), so when the client order a product,
he knows nothing about our subsystem:
@Stateless
public class
FacadeProduct {
@Inject
private InventoryProduct inventoryProduct;
@Inject
private DeliveryProduct deliveryProduct;
@Inject
private DiscountProduct discountProduct;
public int orderProduct(String product) {
if
(inventoryProduct.productIsAvailable(product)) {
int price =
inventoryProduct.productCost(product);
int discount_price =
discountProduct.discountPeriodOfTheYear() ?
discountProduct.applyDiscount(product) : price;
int final_price =
deliveryProduct.freeDeliveryPeriodOfTheYear() ? discount_price :
deliveryProduct.taxDelivery(discount_price);
return final_price;
}
return -1;
}
}
Now, the
Facade design pattern was implemented and we simply inject it in a CDI managed
bean:
Named
@RequestScoped
public class
ProductBean implements Serializable {
private int price;
@Inject
FacadeProduct facadeProduct;
public int getPrice() {
return price;
}
public void orderProductAction(String product)
{
price = facadeProduct.orderProduct(product);
}
}
As you can
see, the managed bean is not aware of the code complexity behind the #orderProduct() call and
it doesn't contain complex business logic (it is a bad practice to define in
this bean our "unit of work" - in some cases, this can be pretty
large and complex).
A dummy
JSF page can serve as a starting point:
<h:form>
Price: $#{productBean.price}
<h:commandButton value="'Nike LUNAR'
Product Order" action="#{productBean.orderProductAction('Nike
LUNAR')}" />
</h:form>
The
complete code is available on
GitHub.